From 56df9ea9ad0810b8c1e6b581fdf44a55750a5c04 Mon Sep 17 00:00:00 2001 From: Alib Date: Mon, 22 Mar 2010 20:31:41 +0100 Subject: Added Minify to concatenate and minimize css and js file and gzip them. Cleaned directories, moved all css and js in respective folders. --- extensions/jqt.autotitles.js | 51 - extensions/jqt.floaty.js | 92 -- extensions/jqt.location.js | 68 -- extensions/jqt.offline.js | 97 -- extensions/jquery.blockUI.js | 477 -------- extensions/jquery.httpstream.js | 161 --- extensions/jquery.scrollTo-min.js | 11 - extensions/jquery.scrollTo.js | 215 ---- index.html | 14 +- jqtouch/jqtouch.css | 373 ------- jqtouch/jqtouch.js | 624 ----------- jqtouch/jqtouch.min.css | 1 - jqtouch/jqtouch.min.js | 1 - jqtouch/jqtouch.transitions.js | 60 - jqtouch/jquery.1.3.2.min.js | 19 - js/istreamdev.js | 29 +- js/spinningwheel-min.js | 1 - min/.htaccess | 4 + min/config.php | 156 +++ min/groupsConfig.php | 34 + min/index.php | 66 ++ min/lib/FirePHP.php | 1370 +++++++++++++++++++++++ min/lib/HTTP/ConditionalGet.php | 348 ++++++ min/lib/HTTP/Encoder.php | 326 ++++++ min/lib/JSMin.php | 314 ++++++ min/lib/JSMinPlus.php | 1872 ++++++++++++++++++++++++++++++++ min/lib/Minify.php | 532 +++++++++ min/lib/Minify/Build.php | 103 ++ min/lib/Minify/CSS.php | 83 ++ min/lib/Minify/CSS/Compressor.php | 250 +++++ min/lib/Minify/CSS/UriRewriter.php | 270 +++++ min/lib/Minify/Cache/APC.php | 130 +++ min/lib/Minify/Cache/File.php | 125 +++ min/lib/Minify/Cache/Memcache.php | 137 +++ min/lib/Minify/CommentPreserver.php | 90 ++ min/lib/Minify/Controller/Base.php | 202 ++++ min/lib/Minify/Controller/Files.php | 78 ++ min/lib/Minify/Controller/Groups.php | 94 ++ min/lib/Minify/Controller/MinApp.php | 132 +++ min/lib/Minify/Controller/Page.php | 82 ++ min/lib/Minify/Controller/Version1.php | 118 ++ min/lib/Minify/HTML.php | 245 +++++ min/lib/Minify/ImportProcessor.php | 157 +++ min/lib/Minify/Lines.php | 131 +++ min/lib/Minify/Logger.php | 45 + min/lib/Minify/Packer.php | 37 + min/lib/Minify/Source.php | 187 ++++ min/lib/Minify/YUICompressor.php | 139 +++ min/lib/Solar/Dir.php | 199 ++++ themes/jqt/img/activeButton.png | Bin 1827 -> 0 bytes themes/jqt/img/back_button.png | Bin 3756 -> 0 bytes themes/jqt/img/back_button_clicked.png | Bin 3741 -> 0 bytes themes/jqt/img/blueButton.png | Bin 1979 -> 0 bytes themes/jqt/img/button.png | Bin 3315 -> 0 bytes themes/jqt/img/button_clicked.png | Bin 3283 -> 0 bytes themes/jqt/img/chevron.png | Bin 3074 -> 0 bytes themes/jqt/img/chevron_circle.png | Bin 1243 -> 0 bytes themes/jqt/img/grayButton.png | Bin 943 -> 0 bytes themes/jqt/img/greenButton.png | Bin 1935 -> 0 bytes themes/jqt/img/loading.gif | Bin 2536 -> 0 bytes themes/jqt/img/on_off.png | Bin 5235 -> 0 bytes themes/jqt/img/redButton.png | Bin 1927 -> 0 bytes themes/jqt/img/rowhead.png | Bin 171 -> 0 bytes themes/jqt/img/toggle.png | Bin 2815 -> 0 bytes themes/jqt/img/toggleOn.png | Bin 163 -> 0 bytes themes/jqt/img/toolbar.png | Bin 805 -> 0 bytes themes/jqt/img/whiteButton.png | Bin 978 -> 0 bytes themes/jqt/theme.css | 561 ---------- themes/jqt/theme.min.css | 1 - 69 files changed, 8079 insertions(+), 2833 deletions(-) delete mode 100644 extensions/jqt.autotitles.js delete mode 100644 extensions/jqt.floaty.js delete mode 100644 extensions/jqt.location.js delete mode 100644 extensions/jqt.offline.js delete mode 100644 extensions/jquery.blockUI.js delete mode 100644 extensions/jquery.httpstream.js delete mode 100644 extensions/jquery.scrollTo-min.js delete mode 100644 extensions/jquery.scrollTo.js delete mode 100644 jqtouch/jqtouch.css delete mode 100644 jqtouch/jqtouch.js delete mode 100644 jqtouch/jqtouch.min.css delete mode 100644 jqtouch/jqtouch.min.js delete mode 100644 jqtouch/jqtouch.transitions.js delete mode 100644 jqtouch/jquery.1.3.2.min.js delete mode 100644 js/spinningwheel-min.js create mode 100644 min/.htaccess create mode 100644 min/config.php create mode 100644 min/groupsConfig.php create mode 100644 min/index.php create mode 100644 min/lib/FirePHP.php create mode 100644 min/lib/HTTP/ConditionalGet.php create mode 100644 min/lib/HTTP/Encoder.php create mode 100644 min/lib/JSMin.php create mode 100644 min/lib/JSMinPlus.php create mode 100644 min/lib/Minify.php create mode 100644 min/lib/Minify/Build.php create mode 100644 min/lib/Minify/CSS.php create mode 100644 min/lib/Minify/CSS/Compressor.php create mode 100644 min/lib/Minify/CSS/UriRewriter.php create mode 100644 min/lib/Minify/Cache/APC.php create mode 100644 min/lib/Minify/Cache/File.php create mode 100644 min/lib/Minify/Cache/Memcache.php create mode 100644 min/lib/Minify/CommentPreserver.php create mode 100644 min/lib/Minify/Controller/Base.php create mode 100644 min/lib/Minify/Controller/Files.php create mode 100644 min/lib/Minify/Controller/Groups.php create mode 100644 min/lib/Minify/Controller/MinApp.php create mode 100644 min/lib/Minify/Controller/Page.php create mode 100644 min/lib/Minify/Controller/Version1.php create mode 100644 min/lib/Minify/HTML.php create mode 100644 min/lib/Minify/ImportProcessor.php create mode 100644 min/lib/Minify/Lines.php create mode 100644 min/lib/Minify/Logger.php create mode 100644 min/lib/Minify/Packer.php create mode 100644 min/lib/Minify/Source.php create mode 100644 min/lib/Minify/YUICompressor.php create mode 100644 min/lib/Solar/Dir.php delete mode 100644 themes/jqt/img/activeButton.png delete mode 100644 themes/jqt/img/back_button.png delete mode 100644 themes/jqt/img/back_button_clicked.png delete mode 100644 themes/jqt/img/blueButton.png delete mode 100644 themes/jqt/img/button.png delete mode 100644 themes/jqt/img/button_clicked.png delete mode 100644 themes/jqt/img/chevron.png delete mode 100644 themes/jqt/img/chevron_circle.png delete mode 100644 themes/jqt/img/grayButton.png delete mode 100644 themes/jqt/img/greenButton.png delete mode 100644 themes/jqt/img/loading.gif delete mode 100644 themes/jqt/img/on_off.png delete mode 100644 themes/jqt/img/redButton.png delete mode 100644 themes/jqt/img/rowhead.png delete mode 100644 themes/jqt/img/toggle.png delete mode 100644 themes/jqt/img/toggleOn.png delete mode 100644 themes/jqt/img/toolbar.png delete mode 100644 themes/jqt/img/whiteButton.png delete mode 100644 themes/jqt/theme.css delete mode 100644 themes/jqt/theme.min.css diff --git a/extensions/jqt.autotitles.js b/extensions/jqt.autotitles.js deleted file mode 100644 index f10142b..0000000 --- a/extensions/jqt.autotitles.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - - _/ _/_/ _/_/_/_/_/ _/ - _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/ - _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/ - _/ - _/ - - Created by David Kaneda - Documentation and issue tracking on Google Code - - Special thanks to Jonathan Stark - and pinch/zoom - - (c) 2009 by jQTouch project members. - See LICENSE.txt for license. - -*/ - -(function($) { - if ($.jQTouch) - { - $.jQTouch.addExtension(function AutoTitles(jQT){ - - var titleSelector='.toolbar h1'; - - $(function(){ - $('#jqt').bind('pageAnimationStart', function(e, data){ - if (data.direction === 'in'){ - var $title = $(titleSelector, $(e.target)); - var $ref = $(e.target).data('referrer'); - if ($title.length && $ref){ - $title.html($ref.text()); - } - } - }); - }); - - function setTitleSelector(ts){ - titleSelector=ts; - } - - return { - setTitleSelector: setTitleSelector - } - - }); - } -})(jQuery); \ No newline at end of file diff --git a/extensions/jqt.floaty.js b/extensions/jqt.floaty.js deleted file mode 100644 index 2ae8f4d..0000000 --- a/extensions/jqt.floaty.js +++ /dev/null @@ -1,92 +0,0 @@ -/* - - _/ _/_/ _/_/_/_/_/ _/ - _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/ - _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/ - _/ - _/ - - Created by David Kaneda - Documentation and issue tracking on Google Code - - Special thanks to Jonathan Stark - and pinch/zoom - - (c) 2009 by jQTouch project members. - See LICENSE.txt for license. - -*/ - -(function($) { - if ($.jQTouch) - { - $.jQTouch.addExtension(function Floaty(jQT){ - - $.fn.makeFloaty = function(options){ - var defaults = { - align: 'top', - spacing: 20, - time: '.3s' - } - var settings = $.extend({}, defaults, options); - settings.align = (settings.align == 'top') ? 'top' : 'bottom'; - - return this.each(function(){ - var $el = $(this); - - $el.css({ - '-webkit-transition': 'top ' + settings.time + ' ease-in-out', - 'display': 'block', - 'min-height': '0 !important' - }).data('settings', settings); - - $(document).bind('scroll', function(){ - if ($el.data('floatyVisible') === true) - { - $el.scrollFloaty(); - } - }); - $el.scrollFloaty(); - }); - } - - $.fn.scrollFloaty = function(){ - return this.each(function(){ - var $el = $(this); - var settings = $el.data('settings'); - var wHeight = $('html').attr('clientHeight'); // WRONG - - var newY = window.pageYOffset + - ((settings.align == 'top') ? - settings.spacing : wHeight - settings.spacing - $el.get(0).offsetHeight); - - $el.css('top', newY).data('floatyVisible', true); - }); - } - - $.fn.hideFloaty = function(){ - return this.each(function(){ - var $el = $(this); - var oh = $el.get(0).offsetHeight; - - $el.css('top', -oh-10).data('floatyVisible', false); - }); - } - - $.fn.toggleFloaty = function(){ - return this.each(function(){ - var $el = $(this); - if ($el.data('floatyVisible') === true){ - $el.hideFloaty(); - } - else - { - $el.scrollFloaty(); - } - }); - } - }); - } -})(jQuery); \ No newline at end of file diff --git a/extensions/jqt.location.js b/extensions/jqt.location.js deleted file mode 100644 index 29b79a0..0000000 --- a/extensions/jqt.location.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - - _/ _/_/ _/_/_/_/_/ _/ - _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/ - _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/ - _/ - _/ - - Created by David Kaneda - Documentation and issue tracking on Google Code - - Special thanks to Jonathan Stark - and pinch/zoom - - (c) 2009 by jQTouch project members. - See LICENSE.txt for license. - -*/ - -(function($) { - if ($.jQTouch) - { - $.jQTouch.addExtension(function Location(){ - - var latitude, longitude, callback; - - function checkGeoLocation() { - return navigator.geolocation; - } - function updateLocation(fn) { - if (checkGeoLocation()) - { - callback = fn; - navigator.geolocation.getCurrentPosition(savePosition); - return true; - } else { - console.log('Device not capable of geo-location.'); - fn(false); - return false; - } - } - function savePosition(position) { - latitude = position.coords.latitude; - longitude = position.coords.longitude; - if (callback) { - callback(getLocation()); - } - } - function getLocation() { - if (latitude && longitude) { - return { - latitude: latitude, - longitude: longitude - } - } else { - console.log('No location available. Try calling updateLocation() first.'); - return false; - } - } - return { - updateLocation: updateLocation, - getLocation: getLocation - } - }); - } -})(jQuery); \ No newline at end of file diff --git a/extensions/jqt.offline.js b/extensions/jqt.offline.js deleted file mode 100644 index b333a16..0000000 --- a/extensions/jqt.offline.js +++ /dev/null @@ -1,97 +0,0 @@ -/* - - _/ _/_/ _/_/_/_/_/ _/ - _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/ - _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/ - _/ - _/ - - Created by David Kaneda - Documentation and issue tracking on Google Code - - Special thanks to Jonathan Stark - - Lots of this code is specifically derived from Jonathan's book, - "Building iPhone Apps with HTML, CSS, and JavaScript" - - (c) 2009 by jQTouch project members. - See LICENSE.txt for license. - -*/ - -(function($) { - if ($.jQTouch) - { - $.jQTouch.addExtension(function Offline(){ - - // Convenience array of status values - var cacheStatusValues = []; - cacheStatusValues[0] = 'uncached'; - cacheStatusValues[1] = 'idle'; - cacheStatusValues[2] = 'checking'; - cacheStatusValues[3] = 'downloading'; - cacheStatusValues[4] = 'updateready'; - cacheStatusValues[5] = 'obsolete'; - - // Listeners for all possible events - var cache = window.applicationCache; - cache.addEventListener('cached', logEvent, false); - cache.addEventListener('checking', logEvent, false); - cache.addEventListener('downloading', logEvent, false); - cache.addEventListener('error', logEvent, false); - cache.addEventListener('noupdate', logEvent, false); - cache.addEventListener('obsolete', logEvent, false); - cache.addEventListener('progress', logEvent, false); - cache.addEventListener('updateready', logEvent, false); - - // Log every event to the console - function logEvent(e) { - var online, status, type, message; - online = (isOnline()) ? 'yes' : 'no'; - status = cacheStatusValues[cache.status]; - type = e.type; - message = 'online: ' + online; - message+= ', event: ' + type; - message+= ', status: ' + status; - if (type == 'error' && navigator.onLine) { - message+= ' There was an unknown error, check your Cache Manifest.'; - } - console.log(message); - } - - function isOnline() { - return navigator.onLine; - } - - if (!$('html').attr('manifest')) { - console.log('No Cache Manifest listed on the tag.') - } - - // Swap in newly download files when update is ready - cache.addEventListener('updateready', function(e){ - // Don't perform "swap" if this is the first cache - if (cacheStatusValues[cache.status] != 'idle') { - cache.swapCache(); - console.log('Swapped/updated the Cache Manifest.'); - } - } - , false); - - // These two functions check for updates to the manifest file - function checkForUpdates(){ - cache.update(); - } - function autoCheckForUpdates(){ - setInterval(function(){cache.update()}, 10000); - } - - return { - isOnline: isOnline, - checkForUpdates: checkForUpdates, - autoCheckForUpdates: autoCheckForUpdates - } - }); - } -})(jQuery); \ No newline at end of file diff --git a/extensions/jquery.blockUI.js b/extensions/jquery.blockUI.js deleted file mode 100644 index f35de1c..0000000 --- a/extensions/jquery.blockUI.js +++ /dev/null @@ -1,477 +0,0 @@ -/*! - * jQuery blockUI plugin - * Version 2.31 (06-JAN-2010) - * @requires jQuery v1.2.3 or later - * - * Examples at: http://malsup.com/jquery/block/ - * Copyright (c) 2007-2008 M. Alsup - * Dual licensed under the MIT and GPL licenses: - * http://www.opensource.org/licenses/mit-license.php - * http://www.gnu.org/licenses/gpl.html - * - * Thanks to Amir-Hossein Sobhi for some excellent contributions! - */ - -;(function($) { - -if (/1\.(0|1|2)\.(0|1|2)/.test($.fn.jquery) || /^1.1/.test($.fn.jquery)) { - alert('blockUI requires jQuery v1.2.3 or later! You are using v' + $.fn.jquery); - return; -} - -$.fn._fadeIn = $.fn.fadeIn; - -var noOp = function() {}; - -// this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle -// retarded userAgent strings on Vista) -var mode = document.documentMode || 0; -var setExpr = $.browser.msie && (($.browser.version < 8 && !mode) || mode < 8); -var ie6 = $.browser.msie && /MSIE 6.0/.test(navigator.userAgent) && !mode; - -// global $ methods for blocking/unblocking the entire page -$.blockUI = function(opts) { install(window, opts); }; -$.unblockUI = function(opts) { remove(window, opts); }; - -// convenience method for quick growl-like notifications (http://www.google.com/search?q=growl) -$.growlUI = function(title, message, timeout, onClose) { - var $m = $('
'); - if (title) $m.append('

'+title+'

'); - if (message) $m.append('

'+message+'

'); - if (timeout == undefined) timeout = 3000; - $.blockUI({ - message: $m, fadeIn: 700, fadeOut: 1000, centerY: false, - timeout: timeout, showOverlay: false, - onUnblock: onClose, - css: $.blockUI.defaults.growlCSS - }); -}; - -// plugin method for blocking element content -$.fn.block = function(opts) { - return this.unblock({ fadeOut: 0 }).each(function() { - if ($.css(this,'position') == 'static') - this.style.position = 'relative'; - if ($.browser.msie) - this.style.zoom = 1; // force 'hasLayout' - install(this, opts); - }); -}; - -// plugin method for unblocking element content -$.fn.unblock = function(opts) { - return this.each(function() { - remove(this, opts); - }); -}; - -$.blockUI.version = 2.31; // 2nd generation blocking at no extra cost! - -// override these in your code to change the default behavior and style -$.blockUI.defaults = { - // message displayed when blocking (use null for no message) - message: '

Please wait...

', - - title: null, // title string; only used when theme == true - draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded) - - theme: false, // set to true to use with jQuery UI themes - - // styles for the message when blocking; if you wish to disable - // these and use an external stylesheet then do this in your code: - // $.blockUI.defaults.css = {}; - css: { - padding: 0, - margin: 0, - width: '30%', - top: '40%', - left: '35%', - textAlign: 'center', - color: '#000', - border: '3px solid #aaa', - backgroundColor:'#fff', - cursor: 'wait' - }, - - // minimal style set used when themes are used - themedCSS: { - width: '30%', - top: '40%', - left: '35%' - }, - - // styles for the overlay - overlayCSS: { - backgroundColor: '#000', - opacity: 0.6, - cursor: 'wait' - }, - - // styles applied when using $.growlUI - growlCSS: { - width: '350px', - top: '10px', - left: '', - right: '10px', - border: 'none', - padding: '5px', - opacity: 0.6, - cursor: 'default', - color: '#fff', - backgroundColor: '#000', - '-webkit-border-radius': '10px', - '-moz-border-radius': '10px' - }, - - // IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w - // (hat tip to Jorge H. N. de Vasconcelos) - iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank', - - // force usage of iframe in non-IE browsers (handy for blocking applets) - forceIframe: false, - - // z-index for the blocking overlay - baseZ: 1000, - - // set these to true to have the message automatically centered - centerX: true, // <-- only effects element blocking (page block controlled via css above) - centerY: true, - - // allow body element to be stetched in ie6; this makes blocking look better - // on "short" pages. disable if you wish to prevent changes to the body height - allowBodyStretch: true, - - // enable if you want key and mouse events to be disabled for content that is blocked - bindEvents: true, - - // be default blockUI will supress tab navigation from leaving blocking content - // (if bindEvents is true) - constrainTabKey: true, - - // fadeIn time in millis; set to 0 to disable fadeIn on block - fadeIn: 200, - - // fadeOut time in millis; set to 0 to disable fadeOut on unblock - fadeOut: 400, - - // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock - timeout: 0, - - // disable if you don't want to show the overlay - showOverlay: true, - - // if true, focus will be placed in the first available input field when - // page blocking - focusInput: true, - - // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity) - applyPlatformOpacityRules: true, - - // callback method invoked when fadeIn has completed and blocking message is visible - onBlock: null, - - // callback method invoked when unblocking has completed; the callback is - // passed the element that has been unblocked (which is the window object for page - // blocks) and the options that were passed to the unblock call: - // onUnblock(element, options) - onUnblock: null, - - // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493 - quirksmodeOffsetHack: 4 -}; - -// private data and functions follow... - -var pageBlock = null; -var pageBlockEls = []; - -function install(el, opts) { - var full = (el == window); - var msg = opts && opts.message !== undefined ? opts.message : undefined; - opts = $.extend({}, $.blockUI.defaults, opts || {}); - opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {}); - var css = $.extend({}, $.blockUI.defaults.css, opts.css || {}); - var themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {}); - msg = msg === undefined ? opts.message : msg; - - // remove the current block (if there is one) - if (full && pageBlock) - remove(window, {fadeOut:0}); - - // if an existing element is being used as the blocking content then we capture - // its current place in the DOM (and current display style) so we can restore - // it when we unblock - if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) { - var node = msg.jquery ? msg[0] : msg; - var data = {}; - $(el).data('blockUI.history', data); - data.el = node; - data.parent = node.parentNode; - data.display = node.style.display; - data.position = node.style.position; - if (data.parent) - data.parent.removeChild(node); - } - - var z = opts.baseZ; - - // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform; - // layer1 is the iframe layer which is used to supress bleed through of underlying content - // layer2 is the overlay layer which has opacity and a wait cursor (by default) - // layer3 is the message content that is displayed while blocking - - var lyr1 = ($.browser.msie || opts.forceIframe) - ? $('') - : $(''); - var lyr2 = $(''); - - var lyr3; - if (opts.theme && full) { - var s = ''; - lyr3 = $(s); - } - else { - lyr3 = full ? $('') - : $(''); - } - - // if we have a message, style it - if (msg) { - if (opts.theme) { - lyr3.css(themedCSS); - lyr3.addClass('ui-widget-content'); - } - else - lyr3.css(css); - } - - // style the overlay - if (!opts.applyPlatformOpacityRules || !($.browser.mozilla && /Linux/.test(navigator.platform))) - lyr2.css(opts.overlayCSS); - lyr2.css('position', full ? 'fixed' : 'absolute'); - - // make iframe layer transparent in IE - if ($.browser.msie || opts.forceIframe) - lyr1.css('opacity',0.0); - - //$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el); - var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el); - $.each(layers, function() { - this.appendTo($par); - }); - - if (opts.theme && opts.draggable && $.fn.draggable) { - lyr3.draggable({ - handle: '.ui-dialog-titlebar', - cancel: 'li' - }); - } - - // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling) - var expr = setExpr && (!$.boxModel || $('object,embed', full ? null : el).length > 0); - if (ie6 || expr) { - // give body 100% height - if (full && opts.allowBodyStretch && $.boxModel) - $('html,body').css('height','100%'); - - // fix ie6 issue when blocked element has a border width - if ((ie6 || !$.boxModel) && !full) { - var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth'); - var fixT = t ? '(0 - '+t+')' : 0; - var fixL = l ? '(0 - '+l+')' : 0; - } - - // simulate fixed position - $.each([lyr1,lyr2,lyr3], function(i,o) { - var s = o[0].style; - s.position = 'absolute'; - if (i < 2) { - full ? s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"') - : s.setExpression('height','this.parentNode.offsetHeight + "px"'); - full ? s.setExpression('width','jQuery.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"') - : s.setExpression('width','this.parentNode.offsetWidth + "px"'); - if (fixL) s.setExpression('left', fixL); - if (fixT) s.setExpression('top', fixT); - } - else if (opts.centerY) { - if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'); - s.marginTop = 0; - } - else if (!opts.centerY && full) { - var top = (opts.css && opts.css.top) ? parseInt(opts.css.top) : 0; - var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"'; - s.setExpression('top',expression); - } - }); - } - - // show the message - if (msg) { - if (opts.theme) - lyr3.find('.ui-widget-content').append(msg); - else - lyr3.append(msg); - if (msg.jquery || msg.nodeType) - $(msg).show(); - } - - if (($.browser.msie || opts.forceIframe) && opts.showOverlay) - lyr1.show(); // opacity is zero - if (opts.fadeIn) { - var cb = opts.onBlock ? opts.onBlock : noOp; - var cb1 = (opts.showOverlay && !msg) ? cb : noOp; - var cb2 = msg ? cb : noOp; - if (opts.showOverlay) - lyr2._fadeIn(opts.fadeIn, cb1); - if (msg) - lyr3._fadeIn(opts.fadeIn, cb2); - } - else { - if (opts.showOverlay) - lyr2.show(); - if (msg) - lyr3.show(); - if (opts.onBlock) - opts.onBlock(); - } - - // bind key and mouse events - bind(1, el, opts); - - if (full) { - pageBlock = lyr3[0]; - pageBlockEls = $(':input:enabled:visible',pageBlock); - if (opts.focusInput) - setTimeout(focus, 20); - } - else - center(lyr3[0], opts.centerX, opts.centerY); - - if (opts.timeout) { - // auto-unblock - var to = setTimeout(function() { - full ? $.unblockUI(opts) : $(el).unblock(opts); - }, opts.timeout); - $(el).data('blockUI.timeout', to); - } -}; - -// remove the block -function remove(el, opts) { - var full = (el == window); - var $el = $(el); - var data = $el.data('blockUI.history'); - var to = $el.data('blockUI.timeout'); - if (to) { - clearTimeout(to); - $el.removeData('blockUI.timeout'); - } - opts = $.extend({}, $.blockUI.defaults, opts || {}); - bind(0, el, opts); // unbind events - - var els; - if (full) // crazy selector to handle odd field errors in ie6/7 - els = $('body').children().filter('.blockUI').add('body > .blockUI'); - else - els = $('.blockUI', el); - - if (full) - pageBlock = pageBlockEls = null; - - if (opts.fadeOut) { - els.fadeOut(opts.fadeOut); - setTimeout(function() { reset(els,data,opts,el); }, opts.fadeOut); - } - else - reset(els, data, opts, el); -}; - -// move blocking element back into the DOM where it started -function reset(els,data,opts,el) { - els.each(function(i,o) { - // remove via DOM calls so we don't lose event handlers - if (this.parentNode) - this.parentNode.removeChild(this); - }); - - if (data && data.el) { - data.el.style.display = data.display; - data.el.style.position = data.position; - if (data.parent) - data.parent.appendChild(data.el); - $(el).removeData('blockUI.history'); - } - - if (typeof opts.onUnblock == 'function') - opts.onUnblock(el,opts); -}; - -// bind/unbind the handler -function bind(b, el, opts) { - var full = el == window, $el = $(el); - - // don't bother unbinding if there is nothing to unbind - if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked'))) - return; - if (!full) - $el.data('blockUI.isBlocked', b); - - // don't bind events when overlay is not in use or if bindEvents is false - if (!opts.bindEvents || (b && !opts.showOverlay)) - return; - - // bind anchors and inputs for mouse and key events - var events = 'mousedown mouseup keydown keypress'; - b ? $(document).bind(events, opts, handler) : $(document).unbind(events, handler); - -// former impl... -// var $e = $('a,:input'); -// b ? $e.bind(events, opts, handler) : $e.unbind(events, handler); -}; - -// event handler to suppress keyboard/mouse events when blocking -function handler(e) { - // allow tab navigation (conditionally) - if (e.keyCode && e.keyCode == 9) { - if (pageBlock && e.data.constrainTabKey) { - var els = pageBlockEls; - var fwd = !e.shiftKey && e.target == els[els.length-1]; - var back = e.shiftKey && e.target == els[0]; - if (fwd || back) { - setTimeout(function(){focus(back)},10); - return false; - } - } - } - // allow events within the message content - if ($(e.target).parents('div.blockMsg').length > 0) - return true; - - // allow events for content that is not being blocked - return $(e.target).parents().children().filter('div.blockUI').length == 0; -}; - -function focus(back) { - if (!pageBlockEls) - return; - var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0]; - if (e) - e.focus(); -}; - -function center(el, x, y) { - var p = el.parentNode, s = el.style; - var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth'); - var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth'); - if (x) s.left = l > 0 ? (l+'px') : '0'; - if (y) s.top = t > 0 ? (t+'px') : '0'; -}; - -function sz(el, p) { - return parseInt($.css(el,p))||0; -}; - -})(jQuery); diff --git a/extensions/jquery.httpstream.js b/extensions/jquery.httpstream.js deleted file mode 100644 index e1be612..0000000 --- a/extensions/jquery.httpstream.js +++ /dev/null @@ -1,161 +0,0 @@ -/* jQuery ajax stream plugin -* Version 0.1 -* Copyright (C) 2009 Chris Tarquini -* Licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License (http://creativecommons.org/licenses/by-sa/3.0/) -* Permissions beyond the scope of this license may be available by contacting petros000[at]hotmail.com. -*/ - -(function($) { - -// Save the original AJAX function -var ajax_old = $.ajax; -var get_old = $.get; -var post_old = $.post; -var active = true; -// Add our settings -$.ajaxSetup({stream: false,pollInterval: 500/*, onDataRecieved: function(){}*/ }); -$.enableAjaxStream = function(enable) -{ -if(typeof enable == 'undefined') enable = !active; -if(!enable) -{ -$.ajax = ajax_old; -$.get = get_old; -$.post = post_old; -active = false; -} -else -{ -$.ajax = ajax_stream; -$.get = ajax_get_stream; -$.post = ajax_post_stream; -active = true; -} - - -} -var ajax_stream = $.ajax = function(options) -{ -//copied from original ajax function - options = jQuery.extend(true, options, jQuery.extend(true, {}, jQuery.ajaxSettings, options)); -if(options.stream) -{ -var timer = 0; -var offset = 0; -var xmlhttp = null; -var lastlen = 0; -var done = false; -var hook = function(xhr) -{ -xmlhttp = xhr; -checkagain(); -} -var fix = function(){ check('stream'); };// fixes weird bug with random numbers as arg -var checkagain = function(){if(!done) timer = setTimeout(fix,options.pollInterval);} -var check = function(status) -{ -if(typeof status == 'undefined') status = "stream"; -if(xmlhttp.status < 3) return; //only get the latest packet if data has been sent -var text = xmlhttp.responseText; -if(status == 'stream') //if we arent streaming then just flush the buffer -{ -if(text.length <= lastlen) { checkagain(); return;} -lastlength = text.length; -if(offset == text.length) { checkagain(); return;} -} -var pkt = text.substr(offset); -offset = text.length; -if($.isFunction(options.OnDataRecieved)) -{ -options.OnDataRecieved(pkt, status, xmlhttp.responseText, xmlhttp); -} -if(xmlhttp.status != 4) -checkagain(); -} -var complete = function(xhr,s) -{ -clearTimeout(timer);//done..stop polling -done = true; -// send final call -check(s); -} -// If the complete callback is set create a new callback that calls the users and outs -if($.isFunction(options.success)) -{ -var oc = options.success; -options.success = function(xhr,s){ complete(xhr,s); oc(xhr,s);}; - -} -else options.success = complete; -// Set up our hook on the beforeSend -if($.isFunction(options.beforeSend)) -{ -var obs = options.beforeSend; -options.beforeSend = function(xhr){ obs(xhr); hook(xhr);}; -} -else options.beforeSend = hook; - -} -ajax_old(options); -} - -var ajax_get_stream = $.get = function(url,data,callback,type,stream) -{ -if($.isFunction(data)) -{ -var orgcb = callback; -callback = data; -if($.isFunction(orgcb)) -{ -stream = orgcb; -} -data = null; - -} -if($.isFunction(type)) -{ -stream = type; -type = undefined; -} -var dostream = $.isFunction(stream); - return jQuery.ajax({ - type: "GET", - url: url, - data: data, - success: callback, - dataType: type, -stream: dostream, -OnDataRecieved: stream - }); - -} - -var ajax_post_stream = $.post = function(url,data,callback,type,stream) -{ - if($.isFunction(data)) - { -var orgcb = callback; - callback = data; - data = {}; -if($.isFunction(orgcb)) { stream = orgcb;} - - } - if($.isFunction(type)) - { - stream = type; - type = undefined; - } - var dostream = $.isFunction(stream); - return jQuery.ajax({ - type: "POST", - url: url, - data: data, - success: callback, - dataType: type, - stream: dostream, - OnDataRecieved: stream - }); - -} - -})(jQuery); diff --git a/extensions/jquery.scrollTo-min.js b/extensions/jquery.scrollTo-min.js deleted file mode 100644 index 73a3341..0000000 --- a/extensions/jquery.scrollTo-min.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * jQuery.ScrollTo - Easy element scrolling using jQuery. - * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com - * Dual licensed under MIT and GPL. - * Date: 5/25/2009 - * @author Ariel Flesler - * @version 1.4.2 - * - * http://flesler.blogspot.com/2007/10/jqueryscrollto.html - */ -;(function(d){var k=d.scrollTo=function(a,i,e){d(window).scrollTo(a,i,e)};k.defaults={axis:'xy',duration:parseFloat(d.fn.jquery)>=1.3?0:1};k.window=function(a){return d(window)._scrollable()};d.fn._scrollable=function(){return this.map(function(){var a=this,i=!a.nodeName||d.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!i)return a;var e=(a.contentWindow||a).document||a.ownerDocument||a;return d.browser.safari||e.compatMode=='BackCompat'?e.body:e.documentElement})};d.fn.scrollTo=function(n,j,b){if(typeof j=='object'){b=j;j=0}if(typeof b=='function')b={onAfter:b};if(n=='max')n=9e9;b=d.extend({},k.defaults,b);j=j||b.speed||b.duration;b.queue=b.queue&&b.axis.length>1;if(b.queue)j/=2;b.offset=p(b.offset);b.over=p(b.over);return this._scrollable().each(function(){var q=this,r=d(q),f=n,s,g={},u=r.is('html,body');switch(typeof f){case'number':case'string':if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(f)){f=p(f);break}f=d(f,this);case'object':if(f.is||f.style)s=(f=d(f)).offset()}d.each(b.axis.split(''),function(a,i){var e=i=='x'?'Left':'Top',h=e.toLowerCase(),c='scroll'+e,l=q[c],m=k.max(q,i);if(s){g[c]=s[h]+(u?0:l-r.offset()[h]);if(b.margin){g[c]-=parseInt(f.css('margin'+e))||0;g[c]-=parseInt(f.css('border'+e+'Width'))||0}g[c]+=b.offset[h]||0;if(b.over[h])g[c]+=f[i=='x'?'width':'height']()*b.over[h]}else{var o=f[h];g[c]=o.slice&&o.slice(-1)=='%'?parseFloat(o)/100*m:o}if(/^\d+$/.test(g[c]))g[c]=g[c]<=0?0:Math.min(g[c],m);if(!a&&b.queue){if(l!=g[c])t(b.onAfterFirst);delete g[c]}});t(b.onAfter);function t(a){r.animate(g,j,b.easing,a&&function(){a.call(this,n,b)})}}).end()};k.max=function(a,i){var e=i=='x'?'Width':'Height',h='scroll'+e;if(!d(a).is('html,body'))return a[h]-d(a)[e.toLowerCase()]();var c='client'+e,l=a.ownerDocument.documentElement,m=a.ownerDocument.body;return Math.max(l[h],m[h])-Math.min(l[c],m[c])};function p(a){return typeof a=='object'?a:{top:a,left:a}}})(jQuery); \ No newline at end of file diff --git a/extensions/jquery.scrollTo.js b/extensions/jquery.scrollTo.js deleted file mode 100644 index eec31e1..0000000 --- a/extensions/jquery.scrollTo.js +++ /dev/null @@ -1,215 +0,0 @@ -/** - * jQuery.ScrollTo - * Copyright (c) 2007-2009 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com - * Dual licensed under MIT and GPL. - * Date: 5/25/2009 - * - * @projectDescription Easy element scrolling using jQuery. - * http://flesler.blogspot.com/2007/10/jqueryscrollto.html - * Works with jQuery +1.2.6. Tested on FF 2/3, IE 6/7/8, Opera 9.5/6, Safari 3, Chrome 1 on WinXP. - * - * @author Ariel Flesler - * @version 1.4.2 - * - * @id jQuery.scrollTo - * @id jQuery.fn.scrollTo - * @param {String, Number, DOMElement, jQuery, Object} target Where to scroll the matched elements. - * The different options for target are: - * - A number position (will be applied to all axes). - * - A string position ('44', '100px', '+=90', etc ) will be applied to all axes - * - A jQuery/DOM element ( logically, child of the element to scroll ) - * - A string selector, that will be relative to the element to scroll ( 'li:eq(2)', etc ) - * - A hash { top:x, left:y }, x and y can be any kind of number/string like above. -* - A percentage of the container's dimension/s, for example: 50% to go to the middle. - * - The string 'max' for go-to-end. - * @param {Number} duration The OVERALL length of the animation, this argument can be the settings object instead. - * @param {Object,Function} settings Optional set of settings or the onAfter callback. - * @option {String} axis Which axis must be scrolled, use 'x', 'y', 'xy' or 'yx'. - * @option {Number} duration The OVERALL length of the animation. - * @option {String} easing The easing method for the animation. - * @option {Boolean} margin If true, the margin of the target element will be deducted from the final position. - * @option {Object, Number} offset Add/deduct from the end position. One number for both axes or { top:x, left:y }. - * @option {Object, Number} over Add/deduct the height/width multiplied by 'over', can be { top:x, left:y } when using both axes. - * @option {Boolean} queue If true, and both axis are given, the 2nd axis will only be animated after the first one ends. - * @option {Function} onAfter Function to be called after the scrolling ends. - * @option {Function} onAfterFirst If queuing is activated, this function will be called after the first scrolling ends. - * @return {jQuery} Returns the same jQuery object, for chaining. - * - * @desc Scroll to a fixed position - * @example $('div').scrollTo( 340 ); - * - * @desc Scroll relatively to the actual position - * @example $('div').scrollTo( '+=340px', { axis:'y' } ); - * - * @dec Scroll using a selector (relative to the scrolled element) - * @example $('div').scrollTo( 'p.paragraph:eq(2)', 500, { easing:'swing', queue:true, axis:'xy' } ); - * - * @ Scroll to a DOM element (same for jQuery object) - * @example var second_child = document.getElementById('container').firstChild.nextSibling; - * $('#container').scrollTo( second_child, { duration:500, axis:'x', onAfter:function(){ - * alert('scrolled!!'); - * }}); - * - * @desc Scroll on both axes, to different values - * @example $('div').scrollTo( { top: 300, left:'+=200' }, { axis:'xy', offset:-20 } ); - */ -;(function( $ ){ - - var $scrollTo = $.scrollTo = function( target, duration, settings ){ - $(window).scrollTo( target, duration, settings ); - }; - - $scrollTo.defaults = { - axis:'xy', - duration: parseFloat($.fn.jquery) >= 1.3 ? 0 : 1 - }; - - // Returns the element that needs to be animated to scroll the window. - // Kept for backwards compatibility (specially for localScroll & serialScroll) - $scrollTo.window = function( scope ){ - return $(window)._scrollable(); - }; - - // Hack, hack, hack :) - // Returns the real elements to scroll (supports window/iframes, documents and regular nodes) - $.fn._scrollable = function(){ - return this.map(function(){ - var elem = this, - isWin = !elem.nodeName || $.inArray( elem.nodeName.toLowerCase(), ['iframe','#document','html','body'] ) != -1; - - if( !isWin ) - return elem; - - var doc = (elem.contentWindow || elem).document || elem.ownerDocument || elem; - - return $.browser.safari || doc.compatMode == 'BackCompat' ? - doc.body : - doc.documentElement; - }); - }; - - $.fn.scrollTo = function( target, duration, settings ){ - if( typeof duration == 'object' ){ - settings = duration; - duration = 0; - } - if( typeof settings == 'function' ) - settings = { onAfter:settings }; - - if( target == 'max' ) - target = 9e9; - - settings = $.extend( {}, $scrollTo.defaults, settings ); - // Speed is still recognized for backwards compatibility - duration = duration || settings.speed || settings.duration; - // Make sure the settings are given right - settings.queue = settings.queue && settings.axis.length > 1; - - if( settings.queue ) - // Let's keep the overall duration - duration /= 2; - settings.offset = both( settings.offset ); - settings.over = both( settings.over ); - - return this._scrollable().each(function(){ - var elem = this, - $elem = $(elem), - targ = target, toff, attr = {}, - win = $elem.is('html,body'); - - switch( typeof targ ){ - // A number will pass the regex - case 'number': - case 'string': - if( /^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(targ) ){ - targ = both( targ ); - // We are done - break; - } - // Relative selector, no break! - targ = $(targ,this); - case 'object': - // DOMElement / jQuery - if( targ.is || targ.style ) - // Get the real position of the target - toff = (targ = $(targ)).offset(); - } - $.each( settings.axis.split(''), function( i, axis ){ - var Pos = axis == 'x' ? 'Left' : 'Top', - pos = Pos.toLowerCase(), - key = 'scroll' + Pos, - old = elem[key], - max = $scrollTo.max(elem, axis); - - if( toff ){// jQuery / DOMElement - attr[key] = toff[pos] + ( win ? 0 : old - $elem.offset()[pos] ); - - // If it's a dom element, reduce the margin - if( settings.margin ){ - attr[key] -= parseInt(targ.css('margin'+Pos)) || 0; - attr[key] -= parseInt(targ.css('border'+Pos+'Width')) || 0; - } - - attr[key] += settings.offset[pos] || 0; - - if( settings.over[pos] ) - // Scroll to a fraction of its width/height - attr[key] += targ[axis=='x'?'width':'height']() * settings.over[pos]; - }else{ - var val = targ[pos]; - // Handle percentage values - attr[key] = val.slice && val.slice(-1) == '%' ? - parseFloat(val) / 100 * max - : val; - } - - // Number or 'number' - if( /^\d+$/.test(attr[key]) ) - // Check the limits - attr[key] = attr[key] <= 0 ? 0 : Math.min( attr[key], max ); - - // Queueing axes - if( !i && settings.queue ){ - // Don't waste time animating, if there's no need. - if( old != attr[key] ) - // Intermediate animation - animate( settings.onAfterFirst ); - // Don't animate this axis again in the next iteration. - delete attr[key]; - } - }); - - animate( settings.onAfter ); - - function animate( callback ){ - $elem.animate( attr, duration, settings.easing, callback && function(){ - callback.call(this, target, settings); - }); - }; - - }).end(); - }; - - // Max scrolling position, works on quirks mode - // It only fails (not too badly) on IE, quirks mode. - $scrollTo.max = function( elem, axis ){ - var Dim = axis == 'x' ? 'Width' : 'Height', - scroll = 'scroll'+Dim; - - if( !$(elem).is('html,body') ) - return elem[scroll] - $(elem)[Dim.toLowerCase()](); - - var size = 'client' + Dim, - html = elem.ownerDocument.documentElement, - body = elem.ownerDocument.body; - - return Math.max( html[scroll], body[scroll] ) - - Math.min( html[size] , body[size] ); - - }; - - function both( val ){ - return typeof val == 'object' ? val : { top:val, left:val }; - }; - -})( jQuery ); \ No newline at end of file diff --git a/index.html b/index.html index dd2eb1b..6ff26e9 100644 --- a/index.html +++ b/index.html @@ -3,23 +3,15 @@ iStreamdev - - - - - - - - - - + +
-
+

HOME

diff --git a/jqtouch/jqtouch.css b/jqtouch/jqtouch.css deleted file mode 100644 index 5c3e736..0000000 --- a/jqtouch/jqtouch.css +++ /dev/null @@ -1,373 +0,0 @@ -* { - margin: 0; - padding: 0; -} -a { - -webkit-tap-highlight-color: rgba(0,0,0,0); -} - -div#jqt { - /*overflow-x: hidden;*/ - -webkit-user-select: none; - -webkit-text-size-adjust: none; - font-family: "Helvetica Neue", Helvetica; - -webkit-perspective: 800; - -webkit-transform-style: preserve-3d; -} -#jqt .selectable, #jqt input, #jqt textarea { - -webkit-user-select: auto; -} -div#jqt > * { - -webkit-backface-visibility: hidden; - -webkit-box-sizing: border-box; - display: none; - position: absolute; - left: 0; - width: 100%; - -webkit-transform: translate3d(0,0,0) rotate(0) scale(1); - min-height: 420px !important; -} -#jqt.fullscreen > * { - min-height: 460px !important; -} -#jqt.fullscreen.black-translucent > * { - min-height: 480px !important; -} -#jqt.landscape > * { - min-height: 320px !important; -} -div#jqt > .current { - display: block !important; -} - -#jqt .in, #jqt .out { - -webkit-animation-timing-function: ease-in-out; - -webkit-animation-duration: 350ms; -} - -#jqt .slide.in { - -webkit-animation-name: slideinfromright; -} - -#jqt .slide.out { - -webkit-animation-name: slideouttoleft; -} - -#jqt .slide.in.reverse { - -webkit-animation-name: slideinfromleft; -} - -#jqt .slide.out.reverse { - -webkit-animation-name: slideouttoright; -} - -@-webkit-keyframes slideinfromright { - from { -webkit-transform: translateX(100%); } - to { -webkit-transform: translateX(0); } -} - -@-webkit-keyframes slideinfromleft { - from { -webkit-transform: translateX(-100%); } - to { -webkit-transform: translateX(0); } -} - -@-webkit-keyframes slideouttoleft { - from { -webkit-transform: translateX(0); } - to { -webkit-transform: translateX(-100%); } -} - -@-webkit-keyframes slideouttoright { - from { -webkit-transform: translateX(0); } - to { -webkit-transform: translateX(100%); } -} - -@-webkit-keyframes fadein { - from { opacity: 0; } - to { opacity: 1; } -} - -@-webkit-keyframes fadeout { - from { opacity: 1; } - to { opacity: 0; } -} - -#jqt .fade.in { - z-index: 10; - -webkit-animation-name: fadein; -} -#jqt .fade.out { - z-index: 0; -} - -#jqt .dissolve.in { - -webkit-animation-name: fadein; -} - -#jqt .dissolve.out { - -webkit-animation-name: fadeout; -} - - - -#jqt .flip { - -webkit-animation-duration: .65s; -} - -#jqt .flip.in { - -webkit-animation-name: flipinfromleft; -} - -#jqt .flip.out { - -webkit-animation-name: flipouttoleft; -} - -/* Shake it all about */ - -#jqt .flip.in.reverse { - -webkit-animation-name: flipinfromright; -} - -#jqt .flip.out.reverse { - -webkit-animation-name: flipouttoright; -} - -@-webkit-keyframes flipinfromright { - from { -webkit-transform: rotateY(-180deg) scale(.8); } - to { -webkit-transform: rotateY(0) scale(1); } -} - -@-webkit-keyframes flipinfromleft { - from { -webkit-transform: rotateY(180deg) scale(.8); } - to { -webkit-transform: rotateY(0) scale(1); } -} - -@-webkit-keyframes flipouttoleft { - from { -webkit-transform: rotateY(0) scale(1); } - to { -webkit-transform: rotateY(-180deg) scale(.8); } -} - -@-webkit-keyframes flipouttoright { - from { -webkit-transform: rotateY(0) scale(1); } - to { -webkit-transform: rotateY(180deg) scale(.8); } -} - -#jqt .slideup.in { - -webkit-animation-name: slideup; - z-index: 10; -} - -#jqt .slideup.out { - -webkit-animation-name: dontmove; - z-index: 0; -} - -#jqt .slideup.out.reverse { - z-index: 10; - -webkit-animation-name: slidedown; -} - -#jqt .slideup.in.reverse { - z-index: 0; - -webkit-animation-name: dontmove; -} - - -@-webkit-keyframes slideup { - from { -webkit-transform: translateY(100%); } - to { -webkit-transform: translateY(0); } -} - -@-webkit-keyframes slidedown { - from { -webkit-transform: translateY(0); } - to { -webkit-transform: translateY(100%); } -} - - - -/* Hackish, but reliable. */ - -@-webkit-keyframes dontmove { - from { opacity: 1; } - to { opacity: 1; } -} - -#jqt .swap { - -webkit-transform: perspective(800); - -webkit-animation-duration: .7s; -} -#jqt .swap.out { - -webkit-animation-name: swapouttoleft; -} -#jqt .swap.in { - -webkit-animation-name: swapinfromright; -} -#jqt .swap.out.reverse { - -webkit-animation-name: swapouttoright; -} -#jqt .swap.in.reverse { - -webkit-animation-name: swapinfromleft; -} - - -@-webkit-keyframes swapouttoright { - 0% { - -webkit-transform: translate3d(0px, 0px, 0px) rotateY(0deg); - -webkit-animation-timing-function: ease-in-out; - } - 50% { - -webkit-transform: translate3d(-180px, 0px, -400px) rotateY(20deg); - -webkit-animation-timing-function: ease-in; - } - 100% { - -webkit-transform: translate3d(0px, 0px, -800px) rotateY(70deg); - } -} - -@-webkit-keyframes swapouttoleft { - 0% { - -webkit-transform: translate3d(0px, 0px, 0px) rotateY(0deg); - -webkit-animation-timing-function: ease-in-out; - } - 50% { - -webkit-transform: translate3d(180px, 0px, -400px) rotateY(-20deg); - -webkit-animation-timing-function: ease-in; - } - 100% { - -webkit-transform: translate3d(0px, 0px, -800px) rotateY(-70deg); - } -} - -@-webkit-keyframes swapinfromright { - 0% { - -webkit-transform: translate3d(0px, 0px, -800px) rotateY(70deg); - -webkit-animation-timing-function: ease-out; - } - 50% { - -webkit-transform: translate3d(-180px, 0px, -400px) rotateY(20deg); - -webkit-animation-timing-function: ease-in-out; - } - 100% { - -webkit-transform: translate3d(0px, 0px, 0px) rotateY(0deg); - } -} - -@-webkit-keyframes swapinfromleft { - 0% { - -webkit-transform: translate3d(0px, 0px, -800px) rotateY(-70deg); - -webkit-animation-timing-function: ease-out; - } - 50% { - -webkit-transform: translate3d(180px, 0px, -400px) rotateY(-20deg); - -webkit-animation-timing-function: ease-in-out; - } - 100% { - -webkit-transform: translate3d(0px, 0px, 0px) rotateY(0deg); - } -} - -#jqt .cube { - -webkit-animation-duration: .55s; -} - -#jqt .cube.in { - -webkit-animation-name: cubeinfromright; - -webkit-transform-origin: 0% 50%; -} -#jqt .cube.out { - -webkit-animation-name: cubeouttoleft; - -webkit-transform-origin: 100% 50%; -} -#jqt .cube.in.reverse { - -webkit-animation-name: cubeinfromleft; - -webkit-transform-origin: 100% 50%; -} -#jqt .cube.out.reverse { - -webkit-animation-name: cubeouttoright; - -webkit-transform-origin: 0% 50%; - -} - -@-webkit-keyframes cubeinfromleft { - from { - -webkit-transform: rotateY(-90deg) translateZ(320px); - opacity: .5; - } - to { - -webkit-transform: rotateY(0deg) translateZ(0); - opacity: 1; - } -} -@-webkit-keyframes cubeouttoright { - from { - -webkit-transform: rotateY(0deg) translateX(0); - opacity: 1; - } - to { - -webkit-transform: rotateY(90deg) translateZ(320px); - opacity: .5; - } -} -@-webkit-keyframes cubeinfromright { - from { - -webkit-transform: rotateY(90deg) translateZ(320px); - opacity: .5; - } - to { - -webkit-transform: rotateY(0deg) translateZ(0); - opacity: 1; - } -} -@-webkit-keyframes cubeouttoleft { - from { - -webkit-transform: rotateY(0deg) translateZ(0); - opacity: 1; - } - to { - -webkit-transform: rotateY(-90deg) translateZ(320px); - opacity: .5; - } -} - - - - -#jqt .pop { - -webkit-transform-origin: 50% 50%; -} - -#jqt .pop.in { - -webkit-animation-name: popin; - z-index: 10; -} - -#jqt .pop.out.reverse { - -webkit-animation-name: popout; - z-index: 10; -} - -#jqt .pop.in.reverse { - z-index: 0; - -webkit-animation-name: dontmove; -} - -@-webkit-keyframes popin { - from { - -webkit-transform: scale(.2); - opacity: 0; - } - to { - -webkit-transform: scale(1); - opacity: 1; - } -} - -@-webkit-keyframes popout { - from { - -webkit-transform: scale(1); - opacity: 1; - } - to { - -webkit-transform: scale(.2); - opacity: 0; - } -} \ No newline at end of file diff --git a/jqtouch/jqtouch.js b/jqtouch/jqtouch.js deleted file mode 100644 index b5057eb..0000000 --- a/jqtouch/jqtouch.js +++ /dev/null @@ -1,624 +0,0 @@ -/* - - _/ _/_/ _/_/_/_/_/ _/ - _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/ - _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/ - _/ - _/ - - Created by David Kaneda - Documentation and issue tracking on Google Code - - Special thanks to Jonathan Stark - and pinch/zoom - - (c) 2009 by jQTouch project members. - See LICENSE.txt for license. - - $Revision: 133 $ - $Date: 2010-01-15 22:08:54 +0100 (ven. 15 janv. 2010) $ - $LastChangedBy: RBoulanouar $ - -*/ - -(function($) { - $.jQTouch = function(options) { - - // Set support values - $.support.WebKitCSSMatrix = (typeof WebKitCSSMatrix == "object"); - $.support.touch = (typeof Touch == "object"); - $.support.WebKitAnimationEvent = (typeof WebKitTransitionEvent == "object"); - - // Initialize internal variables - var $body, - $head=$('head'), - hist=[], - newPageCount=0, - jQTSettings={}, - hashCheckInterval, - currentPage, - orientation, - isMobileWebKit = RegExp(" Mobile/").test(navigator.userAgent), - tapReady=true, - lastAnimationTime=0, - touchSelectors=[], - publicObj={}, - extensions=$.jQTouch.prototype.extensions, - defaultAnimations=['slide','flip','slideup','swap','cube','pop','dissolve','fade','back'], - animations=[], - hairextensions=''; - // Get the party started - init(options); - - function init(options) { - - var defaults = { - addGlossToIcon: true, - backSelector: '.back, .cancel, .goback', - cacheGetRequests: true, - cubeSelector: '.cube', - dissolveSelector: '.dissolve', - fadeSelector: '.fade', - fixedViewport: true, - flipSelector: '.flip', - formSelector: 'form', - fullScreen: true, - fullScreenClass: 'fullscreen', - icon: null, - touchSelector: 'a, .touch', - popSelector: '.pop', - preloadImages: false, - slideSelector: '#jqt > * > ul li a', - slideupSelector: '.slideup', - startupScreen: null, - statusBar: 'default', // other options: black-translucent, black - submitSelector: '.submit', - swapSelector: '.swap', - useAnimations: true, - useFastTouch: true // Experimental. - }; - jQTSettings = $.extend({}, defaults, options); - - // Preload images - if (jQTSettings.preloadImages) { - for (var i = jQTSettings.preloadImages.length - 1; i >= 0; i--) { - (new Image()).src = jQTSettings.preloadImages[i]; - }; - } - // Set icon - if (jQTSettings.icon) { - var precomposed = (jQTSettings.addGlossToIcon) ? '' : '-precomposed'; - hairextensions += ''; - } - // Set startup screen - if (jQTSettings.startupScreen) { - hairextensions += ''; - } - // Set viewport - if (jQTSettings.fixedViewport) { - hairextensions += ''; - } - // Set full-screen - if (jQTSettings.fullScreen) { - hairextensions += ''; - if (jQTSettings.statusBar) { - hairextensions += ''; - } - } - if (hairextensions) { - $head.prepend(hairextensions); - } - - // Initialize on document load: - $(document).ready(function() { - - // Add extensions - for (var i in extensions) { - var fn = extensions[i]; - if ($.isFunction(fn)) { - $.extend(publicObj, fn(publicObj)); - } - } - - // Add animations - for (var i in defaultAnimations) { - var name = defaultAnimations[i]; - var selector = jQTSettings[name + 'Selector']; - if (typeof(selector) == 'string') { - addAnimation({name:name, selector:selector}); - } - } - - touchSelectors.push('input'); - touchSelectors.push(jQTSettings.touchSelector); - touchSelectors.push(jQTSettings.backSelector); - touchSelectors.push(jQTSettings.submitSelector); - $(touchSelectors.join(', ')).css('-webkit-touch-callout', 'none'); - $(jQTSettings.backSelector).tap(liveTap); - $(jQTSettings.submitSelector).tap(submitParentForm); - - $body = $('#jqt'); - - if (jQTSettings.fullScreenClass && window.navigator.standalone == true) { - $body.addClass(jQTSettings.fullScreenClass + ' ' + jQTSettings.statusBar); - } - - // Create custom live events - $body - .bind('touchstart', handleTouch) - .bind('orientationchange', updateOrientation) - .trigger('orientationchange') - .submit(submitForm); - - if (jQTSettings.useFastTouch && $.support.touch) { - $body.click(function(e) { - var $el = $(e.target); - - if ($el.attr('nodeName')!=='A' && $el.attr('nodeName')!=='AREA' && $el.attr('nodeName')!=='INPUT') { - $el = $el.closest('a, area'); - } - - if ($el.isExternalLink()) { - return true; - } else { - return false; - } - }); - - // This additionally gets rid of form focusses - $body.mousedown(function(e) { - var timeDiff = (new Date()).getTime() - lastAnimationTime; - if (timeDiff < 200) { - return false; - } - }); - } - - // Make sure exactly one child of body has "current" class - if ($('#jqt > .current').length == 0) { - currentPage = $('#jqt > *:first'); - } else { - currentPage = $('#jqt > .current:first'); - $('#jqt > .current').removeClass('current'); - } - - // Go to the top of the "current" page - $(currentPage).addClass('current'); - location.hash = '#' + $(currentPage).attr('id'); - addPageToHistory(currentPage); - scrollTo(0, 0); - startHashCheck(); - }); - } - - // PUBLIC FUNCTIONS - function goBack(to) { - // Init the param - var numberOfPages = Math.min(parseInt(to || 1, 10), hist.length-1), - curPage = hist[0]; - - // Search through the history for an ID - if(isNaN(numberOfPages) && typeof(to) === "string" && to != '#' ) { - for( var i=1, length=hist.length; i < length; i++ ) { - if( '#' + hist[i].id === to ) { - numberOfPages = i; - break; - } - } - } - - // If still nothing, assume one - if(isNaN(numberOfPages) || numberOfPages < 1) { - numberOfPages = 1; - }; - - if (hist.length > 1) { - // Remove all pages in front of the target page - hist.splice(0, numberOfPages); - animatePages(curPage.page, hist[0].page, curPage.animation, curPage.reverse === false); - } else { - location.hash = '#' + curPage.id; - } - - return publicObj; - } - function goTo(toPage, animation, reverse) { - var fromPage = hist[0].page; - - if (typeof(toPage) === 'string') { - toPage = $(toPage); - } - if (typeof(animation) === 'string') { - for (var i = animations.length - 1; i >= 0; i--) { - if (animations[i].name === animation) { - animation = animations[i]; - break; - } - } - } - if (animatePages(fromPage, toPage, animation, reverse)) { - addPageToHistory(toPage, animation, reverse); - return publicObj; - } else { - console.error('Could not animate pages.'); - return false; - } - } - function getOrientation() { - return orientation; - } - - // PRIVATE FUNCTIONS - function liveTap(e){ - - // Grab the clicked element - var $el = $(e.target); - - if ($el.attr('nodeName')!=='A' && $el.attr('nodeName')!=='AREA') { - $el = $el.closest('a, area'); - } - - var target = $el.attr('target'), - hash = $el.attr('hash'), - animation=null; - - if (tapReady == false || !$el.length) { - console.warn('Not able to tap element.'); - return false; - } - - if ($el.isExternalLink()) { - $el.removeClass('active'); - return true; - } - - // Figure out the animation to use - for (var i = animations.length - 1; i >= 0; i--) { - if ($el.is(animations[i].selector)) { - animation = animations[i]; - break; - } - }; - - // User clicked an internal link, fullscreen mode - if (target == '_webapp') { - window.location = $el.attr('href'); - } - // User clicked a back button - else if ($el.is(jQTSettings.backSelector)) { - goBack(hash); - } - // Allow tap on item with no href - else if ($el.attr('href') == '#') { - $el.unselect(); - return true; - } - // Branch on internal or external href - else if (hash && hash!='#') { - $el.addClass('active'); - goTo($(hash).data('referrer', $el), animation, $(this).hasClass('reverse')); - } else { - $el.addClass('loading active'); - showPageByHref($el.attr('href'), { - animation: animation, - callback: function() { - $el.removeClass('loading'); setTimeout($.fn.unselect, 250, $el); - }, - $referrer: $el - }); - } - return false; - } - function addPageToHistory(page, animation, reverse) { - // Grab some info - var pageId = page.attr('id'); - // Prepend info to page history - hist.unshift({ - page: page, - animation: animation, - reverse: reverse || false, - id: pageId - }); - } - function animatePages(fromPage, toPage, animation, backwards) { - // Error check for target page - if (toPage.length === 0) { - $.fn.unselect(); - console.error('Target element is missing.'); - return false; - } - - // Error check for fromPage=toPage - if (toPage.hasClass('current')) { - $.fn.unselect(); - console.error('Target element is the current page.'); - return false; - } - - // Collapse the keyboard - $(':focus').blur(); - - // Make sure we are scrolled up to hide location bar - scrollTo(0, 0); - - // Define callback to run after animation completes - var callback = function animationEnd(event) { - if (animation) { - toPage.removeClass('in ' + animation.name); - fromPage.removeClass('current out ' + animation.name); - if (backwards) { - toPage.toggleClass('reverse'); - fromPage.toggleClass('reverse'); - } - } else { - fromPage.removeClass('current'); - } - - toPage.trigger('pageAnimationEnd', { direction: 'in' }); - fromPage.trigger('pageAnimationEnd', { direction: 'out' }); - - clearInterval(hashCheckInterval); - currentPage = toPage; - location.hash = '#' + currentPage.attr('id'); - startHashCheck(); - - var $originallink = toPage.data('referrer'); - if ($originallink) { - $originallink.unselect(); - } - lastAnimationTime = (new Date()).getTime(); - tapReady = true; - } - - fromPage.trigger('pageAnimationStart', { direction: 'out' }); - toPage.trigger('pageAnimationStart', { direction: 'in' }); - - if ($.support.WebKitAnimationEvent && animation && jQTSettings.useAnimations) { - toPage.one('webkitAnimationEnd', callback); - tapReady = false; - if (backwards) { - toPage.toggleClass('reverse'); - fromPage.toggleClass('reverse'); - } - toPage.addClass(animation.name + ' in current '); - fromPage.addClass(animation.name + ' out'); - - } else { - toPage.addClass('current'); - callback(); - } - - return true; - } - function hashCheck() { - var curid = currentPage.attr('id'); - if (location.hash == '') { - location.hash = '#' + curid; - } else if (location.hash != '#' + curid) { - clearInterval(hashCheckInterval); - goBack(location.hash); - } - } - function startHashCheck() { - hashCheckInterval = setInterval(hashCheck, 100); - } - function insertPages(nodes, animation) { - var targetPage = null; - $(nodes).each(function(index, node) { - var $node = $(this); - if (!$node.attr('id')) { - $node.attr('id', 'page-' + (++newPageCount)); - } - - $body.trigger('pageInserted', {page: $node.appendTo($body)}); - - if ($node.hasClass('current') || !targetPage) { - targetPage = $node; - } - }); - if (targetPage !== null) { - goTo(targetPage, animation); - return targetPage; - } else { - return false; - } - } - function showPageByHref(href, options) { - var defaults = { - data: null, - method: 'GET', - animation: null, - callback: null, - $referrer: null - }; - - var settings = $.extend({}, defaults, options); - - if (href != '#') { - $.ajax({ - url: href, - data: settings.data, - type: settings.method, - success: function (data, textStatus) { - var firstPage = insertPages(data, settings.animation); - if (firstPage) { - if (settings.method == 'GET' && jQTSettings.cacheGetRequests === true && settings.$referrer) { - settings.$referrer.attr('href', '#' + firstPage.attr('id')); - } - if (settings.callback) { - settings.callback(true); - } - } - }, - error: function (data) { - if (settings.$referrer) { - settings.$referrer.unselect(); - } - if (settings.callback) { - settings.callback(false); - } - } - }); - } - else if (settings.$referrer) { - settings.$referrer.unselect(); - } - } - function submitForm(e, callback) { - var $form = (typeof(e)==='string') ? $(e).eq(0) : (e.target ? $(e.target) : $(e)); - - if ($form.length && $form.is(jQTSettings.formSelector)) { - showPageByHref($form.attr('action'), { - data: $form.serialize(), - method: $form.attr('method') || "POST", - animation: animations[0] || null, - callback: callback - }); - return false; - } - return true; - } - function submitParentForm(e) { - var $form = $(this).closest('form'); - if ($form.length) { - evt = jQuery.Event("submit"); - evt.preventDefault(); - $form.trigger(evt); - return false; - } - return true; - } - function addAnimation(animation) { - if (typeof(animation.selector) == 'string' && typeof(animation.name) == 'string') { - animations.push(animation); - $(animation.selector).tap(liveTap); - touchSelectors.push(animation.selector); - } - } - function updateOrientation() { - orientation = window.innerWidth < window.innerHeight ? 'profile' : 'landscape'; - $body.removeClass('profile landscape').addClass(orientation).trigger('turn', {orientation: orientation}); - // scrollTo(0, 0); - } - function handleTouch(e) { - var $el = $(e.target); - - // Only handle touchSelectors - if (!$(e.target).is(touchSelectors.join(', '))) { - var $link = $(e.target).closest('a, area'); - - if ($link.length && $link.is(touchSelectors.join(', '))) { - $el = $link; - } else { - return; - } - } - - if (e) { - var hoverTimeout = null, - startX = event.changedTouches[0].clientX, - startY = event.changedTouches[0].clientY, - startTime = (new Date).getTime(), - deltaX = 0, - deltaY = 0, - deltaT = 0; - - // Let's bind these after the fact, so we can keep some internal values - $el.bind('touchmove', touchmove).bind('touchend', touchend); - - hoverTimeout = setTimeout(function() { - $el.makeActive(); - }, 100); - - } - - // Private touch functions (TODO: insert dirty joke) - function touchmove(e) { - - updateChanges(); - var absX = Math.abs(deltaX); - var absY = Math.abs(deltaY); - - // Check for swipe - if (absX > absY && (absX > 35) && deltaT < 1000) { - $el.trigger('swipe', {direction: (deltaX < 0) ? 'left' : 'right', deltaX: deltaX, deltaY: deltaY }).unbind('touchmove touchend'); - } else if (absY > 1) { - $el.removeClass('active'); - } - - clearTimeout(hoverTimeout); - } - - function touchend() { - updateChanges(); - - if (deltaY === 0 && deltaX === 0) { - $el.makeActive(); - $el.trigger('tap'); - } else { - $el.removeClass('active'); - } - $el.unbind('touchmove touchend'); - clearTimeout(hoverTimeout); - } - - function updateChanges() { - var first = event.changedTouches[0] || null; - deltaX = first.pageX - startX; - deltaY = first.pageY - startY; - deltaT = (new Date).getTime() - startTime; - } - - } // End touch handler - - // Public jQuery Fns - $.fn.unselect = function(obj) { - if (obj) { - obj.removeClass('active'); - } else { - $('.active').removeClass('active'); - } - } - $.fn.makeActive = function() { - return $(this).addClass('active'); - } - $.fn.swipe = function(fn) { - if ($.isFunction(fn)) { - return $(this).bind('swipe', fn); - } else { - return $(this).trigger('swipe'); - } - } - $.fn.tap = function(fn) { - if ($.isFunction(fn)) { - var tapEvent = (jQTSettings.useFastTouch && $.support.touch) ? 'tap' : 'click'; - return $(this).live(tapEvent, fn); - } else { - return $(this).trigger('tap'); - } - } - $.fn.isExternalLink = function() { - var $el = $(this); - return ($el.attr('target') == '_blank' || $el.attr('rel') == 'external' || $el.is('input[type="checkbox"], input[type="radio"], a[href^="http://maps.google.com:"], a[href^="mailto:"], a[href^="tel:"], a[href^="javascript:"], a[href*="youtube.com/v"], a[href*="youtube.com/watch"]')); - } - - publicObj = { - getOrientation: getOrientation, - goBack: goBack, - goTo: goTo, - addAnimation: addAnimation, - submitForm: submitForm - } - - return publicObj; - } - - // Extensions directly manipulate the jQTouch object, before it's initialized. - $.jQTouch.prototype.extensions = []; - $.jQTouch.addExtension = function(extension) { - $.jQTouch.prototype.extensions.push(extension); - } - -})(jQuery); \ No newline at end of file diff --git a/jqtouch/jqtouch.min.css b/jqtouch/jqtouch.min.css deleted file mode 100644 index 37ef6fe..0000000 --- a/jqtouch/jqtouch.min.css +++ /dev/null @@ -1 +0,0 @@ -*{margin:0;padding:0;}a{-webkit-tap-highlight-color:rgba(0,0,0,0);}body{overflow-x:hidden;-webkit-user-select:none;-webkit-text-size-adjust:none;font-family:Helvetica;-webkit-perspective:800;-webkit-transform-style:preserve-3d;}.selectable,input,textarea{-webkit-user-select:auto;}body>*{-webkit-backface-visibility:hidden;-webkit-box-sizing:border-box;display:none;position:absolute;left:0;width:100%;-webkit-transform:translate3d(0,0,0) rotate(0) scale(1);min-height:420px!important;}body.fullscreen>*{min-height:460px!important;}body.fullscreen.black-translucent>*{min-height:480px!important;}body.landscape>*{min-height:320px;}body>.current{display:block!important;}.in,.out{-webkit-animation-timing-function:ease-in-out;-webkit-animation-duration:350ms;}.slide.in{-webkit-animation-name:slideinfromright;}.slide.out{-webkit-animation-name:slideouttoleft;}.slide.in.reverse{-webkit-animation-name:slideinfromleft;}.slide.out.reverse{-webkit-animation-name:slideouttoright;}@-webkit-keyframes slideinfromright{from{-webkit-transform:translateX(100%);}to{-webkit-transform:translateX(0);}}@-webkit-keyframes slideinfromleft{from{-webkit-transform:translateX(-100%);}to{-webkit-transform:translateX(0);}}@-webkit-keyframes slideouttoleft{from{-webkit-transform:translateX(0);}to{-webkit-transform:translateX(-100%);}}@-webkit-keyframes slideouttoright{from{-webkit-transform:translateX(0);}to{-webkit-transform:translateX(100%);}}@-webkit-keyframes fadein{from{opacity:0;}to{opacity:1;}}@-webkit-keyframes fadeout{from{opacity:1;}to{opacity:0;}}.fade.in{z-index:10;-webkit-animation-name:fadein;}.fade.out{z-index:0;}.dissolve.in{-webkit-animation-name:fadein;}.dissolve.out{-webkit-animation-name:fadeout;}.flip{-webkit-animation-duration:.65s;}.flip.in{-webkit-animation-name:flipinfromleft;}.flip.out{-webkit-animation-name:flipouttoleft;}.flip.in.reverse{-webkit-animation-name:flipinfromright;}.flip.out.reverse{-webkit-animation-name:flipouttoright;}@-webkit-keyframes flipinfromright{from{-webkit-transform:rotateY(-180deg) scale(.8);}to{-webkit-transform:rotateY(0) scale(1);}}@-webkit-keyframes flipinfromleft{from{-webkit-transform:rotateY(180deg) scale(.8);}to{-webkit-transform:rotateY(0) scale(1);}}@-webkit-keyframes flipouttoleft{from{-webkit-transform:rotateY(0) scale(1);}to{-webkit-transform:rotateY(-180deg) scale(.8);}}@-webkit-keyframes flipouttoright{from{-webkit-transform:rotateY(0) scale(1);}to{-webkit-transform:rotateY(180deg) scale(.8);}}.slideup.in{-webkit-animation-name:slideup;z-index:10;}.slideup.out{-webkit-animation-name:dontmove;z-index:0;}.slideup.out.reverse{z-index:10;-webkit-animation-name:slidedown;}.slideup.in.reverse{z-index:0;-webkit-animation-name:dontmove;}@-webkit-keyframes slideup{from{-webkit-transform:translateY(100%);}to{-webkit-transform:translateY(0);}}@-webkit-keyframes slidedown{from{-webkit-transform:translateY(0);}to{-webkit-transform:translateY(100%);}}@-webkit-keyframes dontmove{from{opacity:1;}to{opacity:1;}}.swap{-webkit-transform:perspective(800);-webkit-animation-duration:.7s;}.swap.out{-webkit-animation-name:swapouttoleft;}.swap.in{-webkit-animation-name:swapinfromright;}.swap.out.reverse{-webkit-animation-name:swapouttoright;}.swap.in.reverse{-webkit-animation-name:swapinfromleft;}@-webkit-keyframes swapouttoright{0%{-webkit-transform:translate3d(0px,0px,0px) rotateY(0deg);-webkit-animation-timing-function:ease-in-out;}50%{-webkit-transform:translate3d(-180px,0px,-400px) rotateY(20deg);-webkit-animation-timing-function:ease-in;}100%{-webkit-transform:translate3d(0px,0px,-800px) rotateY(70deg);}}@-webkit-keyframes swapouttoleft{0%{-webkit-transform:translate3d(0px,0px,0px) rotateY(0deg);-webkit-animation-timing-function:ease-in-out;}50%{-webkit-transform:translate3d(180px,0px,-400px) rotateY(-20deg);-webkit-animation-timing-function:ease-in;}100%{-webkit-transform:translate3d(0px,0px,-800px) rotateY(-70deg);}}@-webkit-keyframes swapinfromright{0%{-webkit-transform:translate3d(0px,0px,-800px) rotateY(70deg);-webkit-animation-timing-function:ease-out;}50%{-webkit-transform:translate3d(-180px,0px,-400px) rotateY(20deg);-webkit-animation-timing-function:ease-in-out;}100%{-webkit-transform:translate3d(0px,0px,0px) rotateY(0deg);}}@-webkit-keyframes swapinfromleft{0%{-webkit-transform:translate3d(0px,0px,-800px) rotateY(-70deg);-webkit-animation-timing-function:ease-out;}50%{-webkit-transform:translate3d(180px,0px,-400px) rotateY(-20deg);-webkit-animation-timing-function:ease-in-out;}100%{-webkit-transform:translate3d(0px,0px,0px) rotateY(0deg);}}.cube{-webkit-animation-duration:.55s;}.cube.in{-webkit-animation-name:cubeinfromright;-webkit-transform-origin:0 50%;}.cube.out{-webkit-animation-name:cubeouttoleft;-webkit-transform-origin:100% 50%;}.cube.in.reverse{-webkit-animation-name:cubeinfromleft;-webkit-transform-origin:100% 50%;}.cube.out.reverse{-webkit-animation-name:cubeouttoright;-webkit-transform-origin:0 50%;}@-webkit-keyframes cubeinfromleft{from{-webkit-transform:rotateY(-90deg) translateZ(320px);opacity:.5;}to{-webkit-transform:rotateY(0deg) translateZ(0);opacity:1;}}@-webkit-keyframes cubeouttoright{from{-webkit-transform:rotateY(0deg) translateX(0);opacity:1;}to{-webkit-transform:rotateY(90deg) translateZ(320px);opacity:.5;}}@-webkit-keyframes cubeinfromright{from{-webkit-transform:rotateY(90deg) translateZ(320px);opacity:.5;}to{-webkit-transform:rotateY(0deg) translateZ(0);opacity:1;}}@-webkit-keyframes cubeouttoleft{from{-webkit-transform:rotateY(0deg) translateZ(0);opacity:1;}to{-webkit-transform:rotateY(-90deg) translateZ(320px);opacity:.5;}}.pop{-webkit-transform-origin:50% 50%;}.pop.in{-webkit-animation-name:popin;z-index:10;}.pop.out.reverse{-webkit-animation-name:popout;z-index:10;}.pop.in.reverse{z-index:0;-webkit-animation-name:dontmove;}@-webkit-keyframes popin{from{-webkit-transform:scale(.2);opacity:0;}to{-webkit-transform:scale(1);opacity:1;}}@-webkit-keyframes popout{from{-webkit-transform:scale(1);opacity:1;}to{-webkit-transform:scale(.2);opacity:0;}} \ No newline at end of file diff --git a/jqtouch/jqtouch.min.js b/jqtouch/jqtouch.min.js deleted file mode 100644 index f43917e..0000000 --- a/jqtouch/jqtouch.min.js +++ /dev/null @@ -1 +0,0 @@ -(function($){$.jQTouch=function(_2){$.support.WebKitCSSMatrix=(typeof WebKitCSSMatrix=="object");$.support.touch=(typeof Touch=="object");$.support.WebKitAnimationEvent=(typeof WebKitTransitionEvent=="object");var _3,$head=$("head"),hist=[],newPageCount=0,jQTSettings={},hashCheck,currentPage,orientation,isMobileWebKit=RegExp(" Mobile/").test(navigator.userAgent),tapReady=true,lastAnimationTime=0,touchSelectors=[],publicObj={},extensions=$.jQTouch.prototype.extensions,defaultAnimations=["slide","flip","slideup","swap","cube","pop","dissolve","fade","back"],animations=[],hairextensions="";init(_2);function init(_4){var _5={addGlossToIcon:true,backSelector:".back, .cancel, .goback",cacheGetRequests:true,cubeSelector:".cube",dissolveSelector:".dissolve",fadeSelector:".fade",fixedViewport:true,flipSelector:".flip",formSelector:"form",fullScreen:true,fullScreenClass:"fullscreen",icon:null,touchSelector:"a, .touch",popSelector:".pop",preloadImages:false,slideSelector:"body > * > ul li a",slideupSelector:".slideup",startupScreen:null,statusBar:"default",submitSelector:".submit",swapSelector:".swap",useAnimations:true,useFastTouch:true};jQTSettings=$.extend({},_5,_4);if(jQTSettings.preloadImages){for(var i=jQTSettings.preloadImages.length-1;i>=0;i--){(new Image()).src=jQTSettings.preloadImages[i];}}if(jQTSettings.icon){var _7=(jQTSettings.addGlossToIcon)?"":"-precomposed";hairextensions+="";}if(jQTSettings.startupScreen){hairextensions+="";}if(jQTSettings.fixedViewport){hairextensions+="";}if(jQTSettings.fullScreen){hairextensions+="";if(jQTSettings.statusBar){hairextensions+="";}}if(hairextensions){$head.append(hairextensions);}$(document).ready(function(){for(var i in extensions){var fn=extensions[i];if($.isFunction(fn)){$.extend(publicObj,fn(publicObj));}}for(var i in defaultAnimations){var _a=defaultAnimations[i];var _b=jQTSettings[_a+"Selector"];if(typeof (_b)=="string"){addAnimation({name:_a,selector:_b});}}touchSelectors.push("input");touchSelectors.push(jQTSettings.touchSelector);touchSelectors.push(jQTSettings.backSelector);touchSelectors.push(jQTSettings.submitSelector);$(touchSelectors.join(", ")).css("-webkit-touch-callout","none");$(jQTSettings.backSelector).tap(liveTap);$(jQTSettings.submitSelector).tap(submitParentForm);_3=$("body");if(jQTSettings.fullScreenClass&&window.navigator.standalone==true){_3.addClass(jQTSettings.fullScreenClass+" "+jQTSettings.statusBar);}_3.bind("touchstart",handleTouch).bind("orientationchange",updateOrientation).trigger("orientationchange").submit(submitForm);if(jQTSettings.useFastTouch&&$.support.touch){_3.click(function(e){var _d=$(e.target);if(_d.attr("target")=="_blank"||_d.attr("rel")=="external"||_d.is("input[type=\"checkbox\"]")){return true;}else{return false;}});_3.mousedown(function(e){var _f=(new Date()).getTime()-lastAnimationTime;if(_f<200){return false;}});}if($("body > .current").length==0){currentPage=$("body > *:first");}else{currentPage=$("body > .current:first");$("body > .current").removeClass("current");}$(currentPage).addClass("current");location.hash=$(currentPage).attr("id");addPageToHistory(currentPage);scrollTo(0,0);dumbLoopStart();});}function goBack(to){if(hist.length>1){var _11=Math.min(parseInt(to||1,10),hist.length-1);if(isNaN(_11)&&typeof (to)==="string"&&to!="#"){for(var i=1,length=hist.length;i=0;i--){if(animations[i].name===_17){_17=animations[i];break;}}}if(animatePages(_18,_16,_17)){addPageToHistory(_16,_17);return publicObj;}else{console.error("Could not animate pages.");return false;}}function getOrientation(){return orientation;}function liveTap(e){var $el=$(e.target);if($el.attr("nodeName")!=="A"){$el=$el.parent("a");}var _1c=$el.attr("target"),hash=$el.attr("hash"),animation=null;if(tapReady==false||!$el.length){console.warn("Not able to tap element.");return false;}if($el.attr("target")=="_blank"||$el.attr("rel")=="external"){return true;}for(var i=animations.length-1;i>=0;i--){if($el.is(animations[i].selector)){animation=animations[i];break;}}if(_1c=="_webapp"){window.location=$el.attr("href");}else{if($el.is(jQTSettings.backSelector)){goBack(hash);}else{if(hash&&hash!="#"){$el.addClass("active");goTo($(hash).data("referrer",$el),animation);}else{$el.addClass("loading active");showPageByHref($el.attr("href"),{animation:animation,callback:function(){$el.removeClass("loading");setTimeout($.fn.unselect,250,$el);},$referrer:$el});}}}return false;}function addPageToHistory(_1e,_1f){var _20=_1e.attr("id");hist.unshift({page:_1e,animation:_1f,id:_20});}function animatePages(_21,_22,_23,_24){if(_22.length===0){$.fn.unselect();console.error("Target element is missing.");return false;}$(":focus").blur();scrollTo(0,0);var _25=function(_26){if(_23){_22.removeClass("in reverse "+_23.name);_21.removeClass("current out reverse "+_23.name);}else{_21.removeClass("current");}_22.trigger("pageAnimationEnd",{direction:"in"});_21.trigger("pageAnimationEnd",{direction:"out"});clearInterval(dumbLoop);currentPage=_22;location.hash=currentPage.attr("id");dumbLoopStart();var _27=_22.data("referrer");if(_27){_27.unselect();}lastAnimationTime=(new Date()).getTime();tapReady=true;};_21.trigger("pageAnimationStart",{direction:"out"});_22.trigger("pageAnimationStart",{direction:"in"});if($.support.WebKitAnimationEvent&&_23&&jQTSettings.useAnimations){_22.one("webkitAnimationEnd",_25);tapReady=false;_22.addClass(_23.name+" in current "+(_24?" reverse":""));_21.addClass(_23.name+" out"+(_24?" reverse":""));}else{_22.addClass("current");_25();}return true;}function dumbLoopStart(){dumbLoop=setInterval(function(){var _28=currentPage.attr("id");if(location.hash==""){location.hash="#"+_28;}else{if(location.hash!="#"+_28){try{goBack(location.hash);}catch(e){console.error("Unknown hash change.");}}}},100);}function insertPages(_29,_2a){var _2b=null;$(_29).each(function(_2c,_2d){var _2e=$(this);if(!_2e.attr("id")){_2e.attr("id","page-"+(++newPageCount));}_2e.appendTo(_3);if(_2e.hasClass("current")||!_2b){_2b=_2e;}});if(_2b!==null){goTo(_2b,_2a);return _2b;}else{return false;}}function showPageByHref(_2f,_30){var _31={data:null,method:"GET",animation:null,callback:null,$referrer:null};var _32=$.extend({},_31,_30);if(_2f!="#"){$.ajax({url:_2f,data:_32.data,type:_32.method,success:function(_33,_34){var _35=insertPages(_33,_32.animation);if(_35){if(_32.method=="GET"&&jQTSettings.cacheGetRequests&&_32.$referrer){_32.$referrer.attr("href","#"+_35.attr("id"));}if(_32.callback){_32.callback(true);}}},error:function(_36){if(_32.$referrer){_32.$referrer.unselect();}if(_32.callback){_32.callback(false);}}});}else{if($referrer){$referrer.unselect();}}}function submitForm(e,_38){var _39=(typeof (e)==="string")?$(e):$(e.target);if(_39.length&&_39.is(jQTSettings.formSelector)&&_39.attr("action")){showPageByHref(_39.attr("action"),{data:_39.serialize(),method:_39.attr("method")||"POST",animation:animations[0]||null,callback:_38});return false;}return true;}function submitParentForm(e){var _3b=$(this).closest("form");if(_3b.length){evt=jQuery.Event("submit");evt.preventDefault();_3b.trigger(evt);return false;}return true;}function addAnimation(_3c){if(typeof (_3c.selector)=="string"&&typeof (_3c.name)=="string"){animations.push(_3c);$(_3c.selector).tap(liveTap);touchSelectors.push(_3c.selector);}}function updateOrientation(){orientation=window.innerWidth_43&&(_42>35)&&deltaT<1000){$el.trigger("swipe",{direction:(deltaX<0)?"left":"right"}).unbind("touchmove touchend");}else{if(_43>1){$el.removeClass("active");}}clearTimeout(_40);}function touchend(){updateChanges();if(deltaY===0&&deltaX===0){$el.makeActive();$el.trigger("tap");}else{$el.removeClass("active");}$el.unbind("touchmove touchend");clearTimeout(_40);}function updateChanges(){var _44=event.changedTouches[0]||null;deltaX=_44.pageX-startX;deltaY=_44.pageY-startY;deltaT=(new Date).getTime()-startTime;}}$.fn.unselect=function(obj){if(obj){obj.removeClass("active");}else{$(".active").removeClass("active");}};$.fn.makeActive=function(){return $(this).addClass("active");};$.fn.swipe=function(fn){if($.isFunction(fn)){return this.each(function(i,el){$(el).bind("swipe",fn);});}};$.fn.tap=function(fn){if($.isFunction(fn)){var _4a=(jQTSettings.useFastTouch&&$.support.touch)?"tap":"click";return $(this).live(_4a,fn);}else{$(this).trigger("tap");}};publicObj={getOrientation:getOrientation,goBack:goBack,goTo:goTo,addAnimation:addAnimation,submitForm:submitForm};return publicObj;};$.jQTouch.prototype.extensions=[];$.jQTouch.addExtension=function(_4b){$.jQTouch.prototype.extensions.push(_4b);};})(jQuery); \ No newline at end of file diff --git a/jqtouch/jqtouch.transitions.js b/jqtouch/jqtouch.transitions.js deleted file mode 100644 index 3fcdc02..0000000 --- a/jqtouch/jqtouch.transitions.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - - _/ _/_/ _/_/_/_/_/ _/ - _/ _/ _/ _/_/ _/ _/ _/_/_/ _/_/_/ - _/ _/ _/_/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/ - _/ _/_/ _/ _/ _/_/ _/_/_/ _/_/_/ _/ _/ - _/ - _/ - - Created by David Kaneda - Documentation and issue tracking on Google Code - - Special thanks to Jonathan Stark - and pinch/zoom - - (c) 2009 by jQTouch project members. - See LICENSE.txt for license. - -*/ - -(function($) { - - $.fn.transition = function(css, options) { - return this.each(function(){ - var $el = $(this); - var defaults = { - speed : '300ms', - callback: null, - ease: 'ease-in-out' - }; - var settings = $.extend({}, defaults, options); - if(settings.speed === 0) { - $el.css(css); - window.setTimeout(settings.callback, 0); - } else { - if ($.browser.safari) - { - var s = []; - for(var i in css) { - s.push(i); - } - $el.css({ - webkitTransitionProperty: s.join(", "), - webkitTransitionDuration: settings.speed, - webkitTransitionTimingFunction: settings.ease - }); - if (settings.callback) { - $el.one('webkitTransitionEnd', settings.callback); - } - setTimeout(function(el){ el.css(css) }, 0, $el); - } - else - { - $el.animate(css, settings.speed, settings.callback); - } - } - }); - } -})(jQuery); \ No newline at end of file diff --git a/jqtouch/jquery.1.3.2.min.js b/jqtouch/jquery.1.3.2.min.js deleted file mode 100644 index 55c2e6d..0000000 --- a/jqtouch/jquery.1.3.2.min.js +++ /dev/null @@ -1,19 +0,0 @@ -/* - * jQuery JavaScript Library v1.3.2 - * http://jquery.com/ - * - * Copyright (c) 2009 John Resig - * Dual licensed under the MIT and GPL licenses. - * http://docs.jquery.com/License - * - * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) - * Revision: 6246 - */ -(function(){var l=this,g,y=l.jQuery,p=l.$,o=l.jQuery=l.$=function(E,F){return new o.fn.init(E,F)},D=/^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/,f=/^.[^:#\[\.,]*$/;o.fn=o.prototype={init:function(E,H){E=E||document;if(E.nodeType){this[0]=E;this.length=1;this.context=E;return this}if(typeof E==="string"){var G=D.exec(E);if(G&&(G[1]||!H)){if(G[1]){E=o.clean([G[1]],H)}else{var I=document.getElementById(G[3]);if(I&&I.id!=G[3]){return o().find(E)}var F=o(I||[]);F.context=document;F.selector=E;return F}}else{return o(H).find(E)}}else{if(o.isFunction(E)){return o(document).ready(E)}}if(E.selector&&E.context){this.selector=E.selector;this.context=E.context}return this.setArray(o.isArray(E)?E:o.makeArray(E))},selector:"",jquery:"1.3.2",size:function(){return this.length},get:function(E){return E===g?Array.prototype.slice.call(this):this[E]},pushStack:function(F,H,E){var G=o(F);G.prevObject=this;G.context=this.context;if(H==="find"){G.selector=this.selector+(this.selector?" ":"")+E}else{if(H){G.selector=this.selector+"."+H+"("+E+")"}}return G},setArray:function(E){this.length=0;Array.prototype.push.apply(this,E);return this},each:function(F,E){return o.each(this,F,E)},index:function(E){return o.inArray(E&&E.jquery?E[0]:E,this)},attr:function(F,H,G){var E=F;if(typeof F==="string"){if(H===g){return this[0]&&o[G||"attr"](this[0],F)}else{E={};E[F]=H}}return this.each(function(I){for(F in E){o.attr(G?this.style:this,F,o.prop(this,E[F],G,I,F))}})},css:function(E,F){if((E=="width"||E=="height")&&parseFloat(F)<0){F=g}return this.attr(E,F,"curCSS")},text:function(F){if(typeof F!=="object"&&F!=null){return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(F))}var E="";o.each(F||this,function(){o.each(this.childNodes,function(){if(this.nodeType!=8){E+=this.nodeType!=1?this.nodeValue:o.fn.text([this])}})});return E},wrapAll:function(E){if(this[0]){var F=o(E,this[0].ownerDocument).clone();if(this[0].parentNode){F.insertBefore(this[0])}F.map(function(){var G=this;while(G.firstChild){G=G.firstChild}return G}).append(this)}return this},wrapInner:function(E){return this.each(function(){o(this).contents().wrapAll(E)})},wrap:function(E){return this.each(function(){o(this).wrapAll(E)})},append:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.appendChild(E)}})},prepend:function(){return this.domManip(arguments,true,function(E){if(this.nodeType==1){this.insertBefore(E,this.firstChild)}})},before:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this)})},after:function(){return this.domManip(arguments,false,function(E){this.parentNode.insertBefore(E,this.nextSibling)})},end:function(){return this.prevObject||o([])},push:[].push,sort:[].sort,splice:[].splice,find:function(E){if(this.length===1){var F=this.pushStack([],"find",E);F.length=0;o.find(E,this[0],F);return F}else{return this.pushStack(o.unique(o.map(this,function(G){return o.find(E,G)})),"find",E)}},clone:function(G){var E=this.map(function(){if(!o.support.noCloneEvent&&!o.isXMLDoc(this)){var I=this.outerHTML;if(!I){var J=this.ownerDocument.createElement("div");J.appendChild(this.cloneNode(true));I=J.innerHTML}return o.clean([I.replace(/ jQuery\d+="(?:\d+|null)"/g,"").replace(/^\s*/,"")])[0]}else{return this.cloneNode(true)}});if(G===true){var H=this.find("*").andSelf(),F=0;E.find("*").andSelf().each(function(){if(this.nodeName!==H[F].nodeName){return}var I=o.data(H[F],"events");for(var K in I){for(var J in I[K]){o.event.add(this,K,I[K][J],I[K][J].data)}}F++})}return E},filter:function(E){return this.pushStack(o.isFunction(E)&&o.grep(this,function(G,F){return E.call(G,F)})||o.multiFilter(E,o.grep(this,function(F){return F.nodeType===1})),"filter",E)},closest:function(E){var G=o.expr.match.POS.test(E)?o(E):null,F=0;return this.map(function(){var H=this;while(H&&H.ownerDocument){if(G?G.index(H)>-1:o(H).is(E)){o.data(H,"closest",F);return H}H=H.parentNode;F++}})},not:function(E){if(typeof E==="string"){if(f.test(E)){return this.pushStack(o.multiFilter(E,this,true),"not",E)}else{E=o.multiFilter(E,this)}}var F=E.length&&E[E.length-1]!==g&&!E.nodeType;return this.filter(function(){return F?o.inArray(this,E)<0:this!=E})},add:function(E){return this.pushStack(o.unique(o.merge(this.get(),typeof E==="string"?o(E):o.makeArray(E))))},is:function(E){return !!E&&o.multiFilter(E,this).length>0},hasClass:function(E){return !!E&&this.is("."+E)},val:function(K){if(K===g){var E=this[0];if(E){if(o.nodeName(E,"option")){return(E.attributes.value||{}).specified?E.value:E.text}if(o.nodeName(E,"select")){var I=E.selectedIndex,L=[],M=E.options,H=E.type=="select-one";if(I<0){return null}for(var F=H?I:0,J=H?I+1:M.length;F=0||o.inArray(this.name,K)>=0)}else{if(o.nodeName(this,"select")){var N=o.makeArray(K);o("option",this).each(function(){this.selected=(o.inArray(this.value,N)>=0||o.inArray(this.text,N)>=0)});if(!N.length){this.selectedIndex=-1}}else{this.value=K}}})},html:function(E){return E===g?(this[0]?this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g,""):null):this.empty().append(E)},replaceWith:function(E){return this.after(E).remove()},eq:function(E){return this.slice(E,+E+1)},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments),"slice",Array.prototype.slice.call(arguments).join(","))},map:function(E){return this.pushStack(o.map(this,function(G,F){return E.call(G,F,G)}))},andSelf:function(){return this.add(this.prevObject)},domManip:function(J,M,L){if(this[0]){var I=(this[0].ownerDocument||this[0]).createDocumentFragment(),F=o.clean(J,(this[0].ownerDocument||this[0]),I),H=I.firstChild;if(H){for(var G=0,E=this.length;G1||G>0?I.cloneNode(true):I)}}if(F){o.each(F,z)}}return this;function K(N,O){return M&&o.nodeName(N,"table")&&o.nodeName(O,"tr")?(N.getElementsByTagName("tbody")[0]||N.appendChild(N.ownerDocument.createElement("tbody"))):N}}};o.fn.init.prototype=o.fn;function z(E,F){if(F.src){o.ajax({url:F.src,async:false,dataType:"script"})}else{o.globalEval(F.text||F.textContent||F.innerHTML||"")}if(F.parentNode){F.parentNode.removeChild(F)}}function e(){return +new Date}o.extend=o.fn.extend=function(){var J=arguments[0]||{},H=1,I=arguments.length,E=false,G;if(typeof J==="boolean"){E=J;J=arguments[1]||{};H=2}if(typeof J!=="object"&&!o.isFunction(J)){J={}}if(I==H){J=this;--H}for(;H-1}},swap:function(H,G,I){var E={};for(var F in G){E[F]=H.style[F];H.style[F]=G[F]}I.call(H);for(var F in G){H.style[F]=E[F]}},css:function(H,F,J,E){if(F=="width"||F=="height"){var L,G={position:"absolute",visibility:"hidden",display:"block"},K=F=="width"?["Left","Right"]:["Top","Bottom"];function I(){L=F=="width"?H.offsetWidth:H.offsetHeight;if(E==="border"){return}o.each(K,function(){if(!E){L-=parseFloat(o.curCSS(H,"padding"+this,true))||0}if(E==="margin"){L+=parseFloat(o.curCSS(H,"margin"+this,true))||0}else{L-=parseFloat(o.curCSS(H,"border"+this+"Width",true))||0}})}if(H.offsetWidth!==0){I()}else{o.swap(H,G,I)}return Math.max(0,Math.round(L))}return o.curCSS(H,F,J)},curCSS:function(I,F,G){var L,E=I.style;if(F=="opacity"&&!o.support.opacity){L=o.attr(E,"opacity");return L==""?"1":L}if(F.match(/float/i)){F=w}if(!G&&E&&E[F]){L=E[F]}else{if(q.getComputedStyle){if(F.match(/float/i)){F="float"}F=F.replace(/([A-Z])/g,"-$1").toLowerCase();var M=q.getComputedStyle(I,null);if(M){L=M.getPropertyValue(F)}if(F=="opacity"&&L==""){L="1"}}else{if(I.currentStyle){var J=F.replace(/\-(\w)/g,function(N,O){return O.toUpperCase()});L=I.currentStyle[F]||I.currentStyle[J];if(!/^\d+(px)?$/i.test(L)&&/^\d/.test(L)){var H=E.left,K=I.runtimeStyle.left;I.runtimeStyle.left=I.currentStyle.left;E.left=L||0;L=E.pixelLeft+"px";E.left=H;I.runtimeStyle.left=K}}}}return L},clean:function(F,K,I){K=K||document;if(typeof K.createElement==="undefined"){K=K.ownerDocument||K[0]&&K[0].ownerDocument||document}if(!I&&F.length===1&&typeof F[0]==="string"){var H=/^<(\w+)\s*\/?>$/.exec(F[0]);if(H){return[K.createElement(H[1])]}}var G=[],E=[],L=K.createElement("div");o.each(F,function(P,S){if(typeof S==="number"){S+=""}if(!S){return}if(typeof S==="string"){S=S.replace(/(<(\w+)[^>]*?)\/>/g,function(U,V,T){return T.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?U:V+">"});var O=S.replace(/^\s+/,"").substring(0,10).toLowerCase();var Q=!O.indexOf("",""]||!O.indexOf("",""]||O.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"","
"]||!O.indexOf("",""]||(!O.indexOf("",""]||!O.indexOf("",""]||!o.support.htmlSerialize&&[1,"div
","
"]||[0,"",""];L.innerHTML=Q[1]+S+Q[2];while(Q[0]--){L=L.lastChild}if(!o.support.tbody){var R=/"&&!R?L.childNodes:[];for(var M=N.length-1;M>=0;--M){if(o.nodeName(N[M],"tbody")&&!N[M].childNodes.length){N[M].parentNode.removeChild(N[M])}}}if(!o.support.leadingWhitespace&&/^\s/.test(S)){L.insertBefore(K.createTextNode(S.match(/^\s*/)[0]),L.firstChild)}S=o.makeArray(L.childNodes)}if(S.nodeType){G.push(S)}else{G=o.merge(G,S)}});if(I){for(var J=0;G[J];J++){if(o.nodeName(G[J],"script")&&(!G[J].type||G[J].type.toLowerCase()==="text/javascript")){E.push(G[J].parentNode?G[J].parentNode.removeChild(G[J]):G[J])}else{if(G[J].nodeType===1){G.splice.apply(G,[J+1,0].concat(o.makeArray(G[J].getElementsByTagName("script"))))}I.appendChild(G[J])}}return E}return G},attr:function(J,G,K){if(!J||J.nodeType==3||J.nodeType==8){return g}var H=!o.isXMLDoc(J),L=K!==g;G=H&&o.props[G]||G;if(J.tagName){var F=/href|src|style/.test(G);if(G=="selected"&&J.parentNode){J.parentNode.selectedIndex}if(G in J&&H&&!F){if(L){if(G=="type"&&o.nodeName(J,"input")&&J.parentNode){throw"type property can't be changed"}J[G]=K}if(o.nodeName(J,"form")&&J.getAttributeNode(G)){return J.getAttributeNode(G).nodeValue}if(G=="tabIndex"){var I=J.getAttributeNode("tabIndex");return I&&I.specified?I.value:J.nodeName.match(/(button|input|object|select|textarea)/i)?0:J.nodeName.match(/^(a|area)$/i)&&J.href?0:g}return J[G]}if(!o.support.style&&H&&G=="style"){return o.attr(J.style,"cssText",K)}if(L){J.setAttribute(G,""+K)}var E=!o.support.hrefNormalized&&H&&F?J.getAttribute(G,2):J.getAttribute(G);return E===null?g:E}if(!o.support.opacity&&G=="opacity"){if(L){J.zoom=1;J.filter=(J.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(K)+""=="NaN"?"":"alpha(opacity="+K*100+")")}return J.filter&&J.filter.indexOf("opacity=")>=0?(parseFloat(J.filter.match(/opacity=([^)]*)/)[1])/100)+"":""}G=G.replace(/-([a-z])/ig,function(M,N){return N.toUpperCase()});if(L){J[G]=K}return J[G]},trim:function(E){return(E||"").replace(/^\s+|\s+$/g,"")},makeArray:function(G){var E=[];if(G!=null){var F=G.length;if(F==null||typeof G==="string"||o.isFunction(G)||G.setInterval){E[0]=G}else{while(F){E[--F]=G[F]}}}return E},inArray:function(G,H){for(var E=0,F=H.length;E0?this.clone(true):this).get();o.fn[F].apply(o(L[K]),I);J=J.concat(I)}return this.pushStack(J,E,G)}});o.each({removeAttr:function(E){o.attr(this,E,"");if(this.nodeType==1){this.removeAttribute(E)}},addClass:function(E){o.className.add(this,E)},removeClass:function(E){o.className.remove(this,E)},toggleClass:function(F,E){if(typeof E!=="boolean"){E=!o.className.has(this,F)}o.className[E?"add":"remove"](this,F)},remove:function(E){if(!E||o.filter(E,[this]).length){o("*",this).add([this]).each(function(){o.event.remove(this);o.removeData(this)});if(this.parentNode){this.parentNode.removeChild(this)}}},empty:function(){o(this).children().remove();while(this.firstChild){this.removeChild(this.firstChild)}}},function(E,F){o.fn[E]=function(){return this.each(F,arguments)}});function j(E,F){return E[0]&&parseInt(o.curCSS(E[0],F,true),10)||0}var h="jQuery"+e(),v=0,A={};o.extend({cache:{},data:function(F,E,G){F=F==l?A:F;var H=F[h];if(!H){H=F[h]=++v}if(E&&!o.cache[H]){o.cache[H]={}}if(G!==g){o.cache[H][E]=G}return E?o.cache[H][E]:H},removeData:function(F,E){F=F==l?A:F;var H=F[h];if(E){if(o.cache[H]){delete o.cache[H][E];E="";for(E in o.cache[H]){break}if(!E){o.removeData(F)}}}else{try{delete F[h]}catch(G){if(F.removeAttribute){F.removeAttribute(h)}}delete o.cache[H]}},queue:function(F,E,H){if(F){E=(E||"fx")+"queue";var G=o.data(F,E);if(!G||o.isArray(H)){G=o.data(F,E,o.makeArray(H))}else{if(H){G.push(H)}}}return G},dequeue:function(H,G){var E=o.queue(H,G),F=E.shift();if(!G||G==="fx"){F=E[0]}if(F!==g){F.call(H)}}});o.fn.extend({data:function(E,G){var H=E.split(".");H[1]=H[1]?"."+H[1]:"";if(G===g){var F=this.triggerHandler("getData"+H[1]+"!",[H[0]]);if(F===g&&this.length){F=o.data(this[0],E)}return F===g&&H[1]?this.data(H[0]):F}else{return this.trigger("setData"+H[1]+"!",[H[0],G]).each(function(){o.data(this,E,G)})}},removeData:function(E){return this.each(function(){o.removeData(this,E)})},queue:function(E,F){if(typeof E!=="string"){F=E;E="fx"}if(F===g){return o.queue(this[0],E)}return this.each(function(){var G=o.queue(this,E,F);if(E=="fx"&&G.length==1){G[0].call(this)}})},dequeue:function(E){return this.each(function(){o.dequeue(this,E)})}}); -/* - * Sizzle CSS Selector Engine - v0.9.3 - * Copyright 2009, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){var R=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g,L=0,H=Object.prototype.toString;var F=function(Y,U,ab,ac){ab=ab||[];U=U||document;if(U.nodeType!==1&&U.nodeType!==9){return[]}if(!Y||typeof Y!=="string"){return ab}var Z=[],W,af,ai,T,ad,V,X=true;R.lastIndex=0;while((W=R.exec(Y))!==null){Z.push(W[1]);if(W[2]){V=RegExp.rightContext;break}}if(Z.length>1&&M.exec(Y)){if(Z.length===2&&I.relative[Z[0]]){af=J(Z[0]+Z[1],U)}else{af=I.relative[Z[0]]?[U]:F(Z.shift(),U);while(Z.length){Y=Z.shift();if(I.relative[Y]){Y+=Z.shift()}af=J(Y,af)}}}else{var ae=ac?{expr:Z.pop(),set:E(ac)}:F.find(Z.pop(),Z.length===1&&U.parentNode?U.parentNode:U,Q(U));af=F.filter(ae.expr,ae.set);if(Z.length>0){ai=E(af)}else{X=false}while(Z.length){var ah=Z.pop(),ag=ah;if(!I.relative[ah]){ah=""}else{ag=Z.pop()}if(ag==null){ag=U}I.relative[ah](ai,ag,Q(U))}}if(!ai){ai=af}if(!ai){throw"Syntax error, unrecognized expression: "+(ah||Y)}if(H.call(ai)==="[object Array]"){if(!X){ab.push.apply(ab,ai)}else{if(U.nodeType===1){for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&(ai[aa]===true||ai[aa].nodeType===1&&K(U,ai[aa]))){ab.push(af[aa])}}}else{for(var aa=0;ai[aa]!=null;aa++){if(ai[aa]&&ai[aa].nodeType===1){ab.push(af[aa])}}}}}else{E(ai,ab)}if(V){F(V,U,ab,ac);if(G){hasDuplicate=false;ab.sort(G);if(hasDuplicate){for(var aa=1;aa":function(Z,U,aa){var X=typeof U==="string";if(X&&!/\W/.test(U)){U=aa?U:U.toUpperCase();for(var V=0,T=Z.length;V=0)){if(!V){T.push(Y)}}else{if(V){U[X]=false}}}}return false},ID:function(T){return T[1].replace(/\\/g,"")},TAG:function(U,T){for(var V=0;T[V]===false;V++){}return T[V]&&Q(T[V])?U[1]:U[1].toUpperCase()},CHILD:function(T){if(T[1]=="nth"){var U=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(T[2]=="even"&&"2n"||T[2]=="odd"&&"2n+1"||!/\D/.test(T[2])&&"0n+"+T[2]||T[2]);T[2]=(U[1]+(U[2]||1))-0;T[3]=U[3]-0}T[0]=L++;return T},ATTR:function(X,U,V,T,Y,Z){var W=X[1].replace(/\\/g,"");if(!Z&&I.attrMap[W]){X[1]=I.attrMap[W]}if(X[2]==="~="){X[4]=" "+X[4]+" "}return X},PSEUDO:function(X,U,V,T,Y){if(X[1]==="not"){if(X[3].match(R).length>1||/^\w/.test(X[3])){X[3]=F(X[3],null,null,U)}else{var W=F.filter(X[3],U,V,true^Y);if(!V){T.push.apply(T,W)}return false}}else{if(I.match.POS.test(X[0])||I.match.CHILD.test(X[0])){return true}}return X},POS:function(T){T.unshift(true);return T}},filters:{enabled:function(T){return T.disabled===false&&T.type!=="hidden"},disabled:function(T){return T.disabled===true},checked:function(T){return T.checked===true},selected:function(T){T.parentNode.selectedIndex;return T.selected===true},parent:function(T){return !!T.firstChild},empty:function(T){return !T.firstChild},has:function(V,U,T){return !!F(T[3],V).length},header:function(T){return/h\d/i.test(T.nodeName)},text:function(T){return"text"===T.type},radio:function(T){return"radio"===T.type},checkbox:function(T){return"checkbox"===T.type},file:function(T){return"file"===T.type},password:function(T){return"password"===T.type},submit:function(T){return"submit"===T.type},image:function(T){return"image"===T.type},reset:function(T){return"reset"===T.type},button:function(T){return"button"===T.type||T.nodeName.toUpperCase()==="BUTTON"},input:function(T){return/input|select|textarea|button/i.test(T.nodeName)}},setFilters:{first:function(U,T){return T===0},last:function(V,U,T,W){return U===W.length-1},even:function(U,T){return T%2===0},odd:function(U,T){return T%2===1},lt:function(V,U,T){return UT[3]-0},nth:function(V,U,T){return T[3]-0==U},eq:function(V,U,T){return T[3]-0==U}},filter:{PSEUDO:function(Z,V,W,aa){var U=V[1],X=I.filters[U];if(X){return X(Z,W,V,aa)}else{if(U==="contains"){return(Z.textContent||Z.innerText||"").indexOf(V[3])>=0}else{if(U==="not"){var Y=V[3];for(var W=0,T=Y.length;W=0)}}},ID:function(U,T){return U.nodeType===1&&U.getAttribute("id")===T},TAG:function(U,T){return(T==="*"&&U.nodeType===1)||U.nodeName===T},CLASS:function(U,T){return(" "+(U.className||U.getAttribute("class"))+" ").indexOf(T)>-1},ATTR:function(Y,W){var V=W[1],T=I.attrHandle[V]?I.attrHandle[V](Y):Y[V]!=null?Y[V]:Y.getAttribute(V),Z=T+"",X=W[2],U=W[4];return T==null?X==="!=":X==="="?Z===U:X==="*="?Z.indexOf(U)>=0:X==="~="?(" "+Z+" ").indexOf(U)>=0:!U?Z&&T!==false:X==="!="?Z!=U:X==="^="?Z.indexOf(U)===0:X==="$="?Z.substr(Z.length-U.length)===U:X==="|="?Z===U||Z.substr(0,U.length+1)===U+"-":false},POS:function(X,U,V,Y){var T=U[2],W=I.setFilters[T];if(W){return W(X,V,U,Y)}}}};var M=I.match.POS;for(var O in I.match){I.match[O]=RegExp(I.match[O].source+/(?![^\[]*\])(?![^\(]*\))/.source)}var E=function(U,T){U=Array.prototype.slice.call(U);if(T){T.push.apply(T,U);return T}return U};try{Array.prototype.slice.call(document.documentElement.childNodes)}catch(N){E=function(X,W){var U=W||[];if(H.call(X)==="[object Array]"){Array.prototype.push.apply(U,X)}else{if(typeof X.length==="number"){for(var V=0,T=X.length;V";var T=document.documentElement;T.insertBefore(U,T.firstChild);if(!!document.getElementById(V)){I.find.ID=function(X,Y,Z){if(typeof Y.getElementById!=="undefined"&&!Z){var W=Y.getElementById(X[1]);return W?W.id===X[1]||typeof W.getAttributeNode!=="undefined"&&W.getAttributeNode("id").nodeValue===X[1]?[W]:g:[]}};I.filter.ID=function(Y,W){var X=typeof Y.getAttributeNode!=="undefined"&&Y.getAttributeNode("id");return Y.nodeType===1&&X&&X.nodeValue===W}}T.removeChild(U)})();(function(){var T=document.createElement("div");T.appendChild(document.createComment(""));if(T.getElementsByTagName("*").length>0){I.find.TAG=function(U,Y){var X=Y.getElementsByTagName(U[1]);if(U[1]==="*"){var W=[];for(var V=0;X[V];V++){if(X[V].nodeType===1){W.push(X[V])}}X=W}return X}}T.innerHTML="";if(T.firstChild&&typeof T.firstChild.getAttribute!=="undefined"&&T.firstChild.getAttribute("href")!=="#"){I.attrHandle.href=function(U){return U.getAttribute("href",2)}}})();if(document.querySelectorAll){(function(){var T=F,U=document.createElement("div");U.innerHTML="

";if(U.querySelectorAll&&U.querySelectorAll(".TEST").length===0){return}F=function(Y,X,V,W){X=X||document;if(!W&&X.nodeType===9&&!Q(X)){try{return E(X.querySelectorAll(Y),V)}catch(Z){}}return T(Y,X,V,W)};F.find=T.find;F.filter=T.filter;F.selectors=T.selectors;F.matches=T.matches})()}if(document.getElementsByClassName&&document.documentElement.getElementsByClassName){(function(){var T=document.createElement("div");T.innerHTML="
";if(T.getElementsByClassName("e").length===0){return}T.lastChild.className="e";if(T.getElementsByClassName("e").length===1){return}I.order.splice(1,0,"CLASS");I.find.CLASS=function(U,V,W){if(typeof V.getElementsByClassName!=="undefined"&&!W){return V.getElementsByClassName(U[1])}}})()}function P(U,Z,Y,ad,aa,ac){var ab=U=="previousSibling"&&!ac;for(var W=0,V=ad.length;W0){X=T;break}}}T=T[U]}ad[W]=X}}}var K=document.compareDocumentPosition?function(U,T){return U.compareDocumentPosition(T)&16}:function(U,T){return U!==T&&(U.contains?U.contains(T):true)};var Q=function(T){return T.nodeType===9&&T.documentElement.nodeName!=="HTML"||!!T.ownerDocument&&Q(T.ownerDocument)};var J=function(T,aa){var W=[],X="",Y,V=aa.nodeType?[aa]:aa;while((Y=I.match.PSEUDO.exec(T))){X+=Y[0];T=T.replace(I.match.PSEUDO,"")}T=I.relative[T]?T+"*":T;for(var Z=0,U=V.length;Z0||T.offsetHeight>0};F.selectors.filters.animated=function(T){return o.grep(o.timers,function(U){return T===U.elem}).length};o.multiFilter=function(V,T,U){if(U){V=":not("+V+")"}return F.matches(V,T)};o.dir=function(V,U){var T=[],W=V[U];while(W&&W!=document){if(W.nodeType==1){T.push(W)}W=W[U]}return T};o.nth=function(X,T,V,W){T=T||1;var U=0;for(;X;X=X[V]){if(X.nodeType==1&&++U==T){break}}return X};o.sibling=function(V,U){var T=[];for(;V;V=V.nextSibling){if(V.nodeType==1&&V!=U){T.push(V)}}return T};return;l.Sizzle=F})();o.event={add:function(I,F,H,K){if(I.nodeType==3||I.nodeType==8){return}if(I.setInterval&&I!=l){I=l}if(!H.guid){H.guid=this.guid++}if(K!==g){var G=H;H=this.proxy(G);H.data=K}var E=o.data(I,"events")||o.data(I,"events",{}),J=o.data(I,"handle")||o.data(I,"handle",function(){return typeof o!=="undefined"&&!o.event.triggered?o.event.handle.apply(arguments.callee.elem,arguments):g});J.elem=I;o.each(F.split(/\s+/),function(M,N){var O=N.split(".");N=O.shift();H.type=O.slice().sort().join(".");var L=E[N];if(o.event.specialAll[N]){o.event.specialAll[N].setup.call(I,K,O)}if(!L){L=E[N]={};if(!o.event.special[N]||o.event.special[N].setup.call(I,K,O)===false){if(I.addEventListener){I.addEventListener(N,J,false)}else{if(I.attachEvent){I.attachEvent("on"+N,J)}}}}L[H.guid]=H;o.event.global[N]=true});I=null},guid:1,global:{},remove:function(K,H,J){if(K.nodeType==3||K.nodeType==8){return}var G=o.data(K,"events"),F,E;if(G){if(H===g||(typeof H==="string"&&H.charAt(0)==".")){for(var I in G){this.remove(K,I+(H||""))}}else{if(H.type){J=H.handler;H=H.type}o.each(H.split(/\s+/),function(M,O){var Q=O.split(".");O=Q.shift();var N=RegExp("(^|\\.)"+Q.slice().sort().join(".*\\.")+"(\\.|$)");if(G[O]){if(J){delete G[O][J.guid]}else{for(var P in G[O]){if(N.test(G[O][P].type)){delete G[O][P]}}}if(o.event.specialAll[O]){o.event.specialAll[O].teardown.call(K,Q)}for(F in G[O]){break}if(!F){if(!o.event.special[O]||o.event.special[O].teardown.call(K,Q)===false){if(K.removeEventListener){K.removeEventListener(O,o.data(K,"handle"),false)}else{if(K.detachEvent){K.detachEvent("on"+O,o.data(K,"handle"))}}}F=null;delete G[O]}}})}for(F in G){break}if(!F){var L=o.data(K,"handle");if(L){L.elem=null}o.removeData(K,"events");o.removeData(K,"handle")}}},trigger:function(I,K,H,E){var G=I.type||I;if(!E){I=typeof I==="object"?I[h]?I:o.extend(o.Event(G),I):o.Event(G);if(G.indexOf("!")>=0){I.type=G=G.slice(0,-1);I.exclusive=true}if(!H){I.stopPropagation();if(this.global[G]){o.each(o.cache,function(){if(this.events&&this.events[G]){o.event.trigger(I,K,this.handle.elem)}})}}if(!H||H.nodeType==3||H.nodeType==8){return g}I.result=g;I.target=H;K=o.makeArray(K);K.unshift(I)}I.currentTarget=H;var J=o.data(H,"handle");if(J){J.apply(H,K)}if((!H[G]||(o.nodeName(H,"a")&&G=="click"))&&H["on"+G]&&H["on"+G].apply(H,K)===false){I.result=false}if(!E&&H[G]&&!I.isDefaultPrevented()&&!(o.nodeName(H,"a")&&G=="click")){this.triggered=true;try{H[G]()}catch(L){}}this.triggered=false;if(!I.isPropagationStopped()){var F=H.parentNode||H.ownerDocument;if(F){o.event.trigger(I,K,F,true)}}},handle:function(K){var J,E;K=arguments[0]=o.event.fix(K||l.event);K.currentTarget=this;var L=K.type.split(".");K.type=L.shift();J=!L.length&&!K.exclusive;var I=RegExp("(^|\\.)"+L.slice().sort().join(".*\\.")+"(\\.|$)");E=(o.data(this,"events")||{})[K.type];for(var G in E){var H=E[G];if(J||I.test(H.type)){K.handler=H;K.data=H.data;var F=H.apply(this,arguments);if(F!==g){K.result=F;if(F===false){K.preventDefault();K.stopPropagation()}}if(K.isImmediatePropagationStopped()){break}}}},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(H){if(H[h]){return H}var F=H;H=o.Event(F);for(var G=this.props.length,J;G;){J=this.props[--G];H[J]=F[J]}if(!H.target){H.target=H.srcElement||document}if(H.target.nodeType==3){H.target=H.target.parentNode}if(!H.relatedTarget&&H.fromElement){H.relatedTarget=H.fromElement==H.target?H.toElement:H.fromElement}if(H.pageX==null&&H.clientX!=null){var I=document.documentElement,E=document.body;H.pageX=H.clientX+(I&&I.scrollLeft||E&&E.scrollLeft||0)-(I.clientLeft||0);H.pageY=H.clientY+(I&&I.scrollTop||E&&E.scrollTop||0)-(I.clientTop||0)}if(!H.which&&((H.charCode||H.charCode===0)?H.charCode:H.keyCode)){H.which=H.charCode||H.keyCode}if(!H.metaKey&&H.ctrlKey){H.metaKey=H.ctrlKey}if(!H.which&&H.button){H.which=(H.button&1?1:(H.button&2?3:(H.button&4?2:0)))}return H},proxy:function(F,E){E=E||function(){return F.apply(this,arguments)};E.guid=F.guid=F.guid||E.guid||this.guid++;return E},special:{ready:{setup:B,teardown:function(){}}},specialAll:{live:{setup:function(E,F){o.event.add(this,F[0],c)},teardown:function(G){if(G.length){var E=0,F=RegExp("(^|\\.)"+G[0]+"(\\.|$)");o.each((o.data(this,"events").live||{}),function(){if(F.test(this.type)){E++}});if(E<1){o.event.remove(this,G[0],c)}}}}}};o.Event=function(E){if(!this.preventDefault){return new o.Event(E)}if(E&&E.type){this.originalEvent=E;this.type=E.type}else{this.type=E}this.timeStamp=e();this[h]=true};function k(){return false}function u(){return true}o.Event.prototype={preventDefault:function(){this.isDefaultPrevented=u;var E=this.originalEvent;if(!E){return}if(E.preventDefault){E.preventDefault()}E.returnValue=false},stopPropagation:function(){this.isPropagationStopped=u;var E=this.originalEvent;if(!E){return}if(E.stopPropagation){E.stopPropagation()}E.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=u;this.stopPropagation()},isDefaultPrevented:k,isPropagationStopped:k,isImmediatePropagationStopped:k};var a=function(F){var E=F.relatedTarget;while(E&&E!=this){try{E=E.parentNode}catch(G){E=this}}if(E!=this){F.type=F.data;o.event.handle.apply(this,arguments)}};o.each({mouseover:"mouseenter",mouseout:"mouseleave"},function(F,E){o.event.special[E]={setup:function(){o.event.add(this,F,a,E)},teardown:function(){o.event.remove(this,F,a)}}});o.fn.extend({bind:function(F,G,E){return F=="unload"?this.one(F,G,E):this.each(function(){o.event.add(this,F,E||G,E&&G)})},one:function(G,H,F){var E=o.event.proxy(F||H,function(I){o(this).unbind(I,E);return(F||H).apply(this,arguments)});return this.each(function(){o.event.add(this,G,E,F&&H)})},unbind:function(F,E){return this.each(function(){o.event.remove(this,F,E)})},trigger:function(E,F){return this.each(function(){o.event.trigger(E,F,this)})},triggerHandler:function(E,G){if(this[0]){var F=o.Event(E);F.preventDefault();F.stopPropagation();o.event.trigger(F,G,this[0]);return F.result}},toggle:function(G){var E=arguments,F=1;while(F=0){var E=G.slice(I,G.length);G=G.slice(0,I)}var H="GET";if(J){if(o.isFunction(J)){K=J;J=null}else{if(typeof J==="object"){J=o.param(J);H="POST"}}}var F=this;o.ajax({url:G,type:H,dataType:"html",data:J,complete:function(M,L){if(L=="success"||L=="notmodified"){F.html(E?o("
").append(M.responseText.replace(//g,"")).find(E):M.responseText)}if(K){F.each(K,[M.responseText,L,M])}}});return this},serialize:function(){return o.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?o.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password|search/i.test(this.type))}).map(function(E,F){var G=o(this).val();return G==null?null:o.isArray(G)?o.map(G,function(I,H){return{name:F.name,value:I}}):{name:F.name,value:G}}).get()}});o.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(E,F){o.fn[F]=function(G){return this.bind(F,G)}});var r=e();o.extend({get:function(E,G,H,F){if(o.isFunction(G)){H=G;G=null}return o.ajax({type:"GET",url:E,data:G,success:H,dataType:F})},getScript:function(E,F){return o.get(E,null,F,"script")},getJSON:function(E,F,G){return o.get(E,F,G,"json")},post:function(E,G,H,F){if(o.isFunction(G)){H=G;G={}}return o.ajax({type:"POST",url:E,data:G,success:H,dataType:F})},ajaxSetup:function(E){o.extend(o.ajaxSettings,E)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return l.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest()},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(M){M=o.extend(true,M,o.extend(true,{},o.ajaxSettings,M));var W,F=/=\?(&|$)/g,R,V,G=M.type.toUpperCase();if(M.data&&M.processData&&typeof M.data!=="string"){M.data=o.param(M.data)}if(M.dataType=="jsonp"){if(G=="GET"){if(!M.url.match(F)){M.url+=(M.url.match(/\?/)?"&":"?")+(M.jsonp||"callback")+"=?"}}else{if(!M.data||!M.data.match(F)){M.data=(M.data?M.data+"&":"")+(M.jsonp||"callback")+"=?"}}M.dataType="json"}if(M.dataType=="json"&&(M.data&&M.data.match(F)||M.url.match(F))){W="jsonp"+r++;if(M.data){M.data=(M.data+"").replace(F,"="+W+"$1")}M.url=M.url.replace(F,"="+W+"$1");M.dataType="script";l[W]=function(X){V=X;I();L();l[W]=g;try{delete l[W]}catch(Y){}if(H){H.removeChild(T)}}}if(M.dataType=="script"&&M.cache==null){M.cache=false}if(M.cache===false&&G=="GET"){var E=e();var U=M.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+E+"$2");M.url=U+((U==M.url)?(M.url.match(/\?/)?"&":"?")+"_="+E:"")}if(M.data&&G=="GET"){M.url+=(M.url.match(/\?/)?"&":"?")+M.data;M.data=null}if(M.global&&!o.active++){o.event.trigger("ajaxStart")}var Q=/^(\w+:)?\/\/([^\/?#]+)/.exec(M.url);if(M.dataType=="script"&&G=="GET"&&Q&&(Q[1]&&Q[1]!=location.protocol||Q[2]!=location.host)){var H=document.getElementsByTagName("head")[0];var T=document.createElement("script");T.src=M.url;if(M.scriptCharset){T.charset=M.scriptCharset}if(!W){var O=false;T.onload=T.onreadystatechange=function(){if(!O&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){O=true;I();L();T.onload=T.onreadystatechange=null;H.removeChild(T)}}}H.appendChild(T);return g}var K=false;var J=M.xhr();if(M.username){J.open(G,M.url,M.async,M.username,M.password)}else{J.open(G,M.url,M.async)}try{if(M.data){J.setRequestHeader("Content-Type",M.contentType)}if(M.ifModified){J.setRequestHeader("If-Modified-Since",o.lastModified[M.url]||"Thu, 01 Jan 1970 00:00:00 GMT")}J.setRequestHeader("X-Requested-With","XMLHttpRequest");J.setRequestHeader("Accept",M.dataType&&M.accepts[M.dataType]?M.accepts[M.dataType]+", */*":M.accepts._default)}catch(S){}if(M.beforeSend&&M.beforeSend(J,M)===false){if(M.global&&!--o.active){o.event.trigger("ajaxStop")}J.abort();return false}if(M.global){o.event.trigger("ajaxSend",[J,M])}var N=function(X){if(J.readyState==0){if(P){clearInterval(P);P=null;if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}}else{if(!K&&J&&(J.readyState==4||X=="timeout")){K=true;if(P){clearInterval(P);P=null}R=X=="timeout"?"timeout":!o.httpSuccess(J)?"error":M.ifModified&&o.httpNotModified(J,M.url)?"notmodified":"success";if(R=="success"){try{V=o.httpData(J,M.dataType,M)}catch(Z){R="parsererror"}}if(R=="success"){var Y;try{Y=J.getResponseHeader("Last-Modified")}catch(Z){}if(M.ifModified&&Y){o.lastModified[M.url]=Y}if(!W){I()}}else{o.handleError(M,J,R)}L();if(X){J.abort()}if(M.async){J=null}}}};if(M.async){var P=setInterval(N,13);if(M.timeout>0){setTimeout(function(){if(J&&!K){N("timeout")}},M.timeout)}}try{J.send(M.data)}catch(S){o.handleError(M,J,null,S)}if(!M.async){N()}function I(){if(M.success){M.success(V,R)}if(M.global){o.event.trigger("ajaxSuccess",[J,M])}}function L(){if(M.complete){M.complete(J,R)}if(M.global){o.event.trigger("ajaxComplete",[J,M])}if(M.global&&!--o.active){o.event.trigger("ajaxStop")}}return J},handleError:function(F,H,E,G){if(F.error){F.error(H,E,G)}if(F.global){o.event.trigger("ajaxError",[H,F,G])}},active:0,httpSuccess:function(F){try{return !F.status&&location.protocol=="file:"||(F.status>=200&&F.status<300)||F.status==304||F.status==1223}catch(E){}return false},httpNotModified:function(G,E){try{var H=G.getResponseHeader("Last-Modified");return G.status==304||H==o.lastModified[E]}catch(F){}return false},httpData:function(J,H,G){var F=J.getResponseHeader("content-type"),E=H=="xml"||!H&&F&&F.indexOf("xml")>=0,I=E?J.responseXML:J.responseText;if(E&&I.documentElement.tagName=="parsererror"){throw"parsererror"}if(G&&G.dataFilter){I=G.dataFilter(I,H)}if(typeof I==="string"){if(H=="script"){o.globalEval(I)}if(H=="json"){I=l["eval"]("("+I+")")}}return I},param:function(E){var G=[];function H(I,J){G[G.length]=encodeURIComponent(I)+"="+encodeURIComponent(J)}if(o.isArray(E)||E.jquery){o.each(E,function(){H(this.name,this.value)})}else{for(var F in E){if(o.isArray(E[F])){o.each(E[F],function(){H(F,this)})}else{H(F,o.isFunction(E[F])?E[F]():E[F])}}}return G.join("&").replace(/%20/g,"+")}});var m={},n,d=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];function t(F,E){var G={};o.each(d.concat.apply([],d.slice(0,E)),function(){G[this]=F});return G}o.fn.extend({show:function(J,L){if(J){return this.animate(t("show",3),J,L)}else{for(var H=0,F=this.length;H").appendTo("body");K=I.css("display");if(K==="none"){K="block"}I.remove();m[G]=K}o.data(this[H],"olddisplay",K)}}for(var H=0,F=this.length;H=0;H--){if(G[H].elem==this){if(E){G[H](true)}G.splice(H,1)}}});if(!E){this.dequeue()}return this}});o.each({slideDown:t("show",1),slideUp:t("hide",1),slideToggle:t("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(E,F){o.fn[E]=function(G,H){return this.animate(F,G,H)}});o.extend({speed:function(G,H,F){var E=typeof G==="object"?G:{complete:F||!F&&H||o.isFunction(G)&&G,duration:G,easing:F&&H||H&&!o.isFunction(H)&&H};E.duration=o.fx.off?0:typeof E.duration==="number"?E.duration:o.fx.speeds[E.duration]||o.fx.speeds._default;E.old=E.complete;E.complete=function(){if(E.queue!==false){o(this).dequeue()}if(o.isFunction(E.old)){E.old.call(this)}};return E},easing:{linear:function(G,H,E,F){return E+F*G},swing:function(G,H,E,F){return((-Math.cos(G*Math.PI)/2)+0.5)*F+E}},timers:[],fx:function(F,E,G){this.options=E;this.elem=F;this.prop=G;if(!E.orig){E.orig={}}}});o.fx.prototype={update:function(){if(this.options.step){this.options.step.call(this.elem,this.now,this)}(o.fx.step[this.prop]||o.fx.step._default)(this);if((this.prop=="height"||this.prop=="width")&&this.elem.style){this.elem.style.display="block"}},cur:function(F){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null)){return this.elem[this.prop]}var E=parseFloat(o.css(this.elem,this.prop,F));return E&&E>-10000?E:parseFloat(o.curCSS(this.elem,this.prop))||0},custom:function(I,H,G){this.startTime=e();this.start=I;this.end=H;this.unit=G||this.unit||"px";this.now=this.start;this.pos=this.state=0;var E=this;function F(J){return E.step(J)}F.elem=this.elem;if(F()&&o.timers.push(F)&&!n){n=setInterval(function(){var K=o.timers;for(var J=0;J=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var E=true;for(var F in this.options.curAnim){if(this.options.curAnim[F]!==true){E=false}}if(E){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(o.css(this.elem,"display")=="none"){this.elem.style.display="block"}}if(this.options.hide){o(this.elem).hide()}if(this.options.hide||this.options.show){for(var I in this.options.curAnim){o.attr(this.elem.style,I,this.options.orig[I])}}this.options.complete.call(this.elem)}return false}else{var J=G-this.startTime;this.state=J/this.options.duration;this.pos=o.easing[this.options.easing||(o.easing.swing?"swing":"linear")](this.state,J,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update()}return true}};o.extend(o.fx,{speeds:{slow:600,fast:200,_default:400},step:{opacity:function(E){o.attr(E.elem.style,"opacity",E.now)},_default:function(E){if(E.elem.style&&E.elem.style[E.prop]!=null){E.elem.style[E.prop]=E.now+E.unit}else{E.elem[E.prop]=E.now}}}});if(document.documentElement.getBoundingClientRect){o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}var G=this[0].getBoundingClientRect(),J=this[0].ownerDocument,F=J.body,E=J.documentElement,L=E.clientTop||F.clientTop||0,K=E.clientLeft||F.clientLeft||0,I=G.top+(self.pageYOffset||o.boxModel&&E.scrollTop||F.scrollTop)-L,H=G.left+(self.pageXOffset||o.boxModel&&E.scrollLeft||F.scrollLeft)-K;return{top:I,left:H}}}else{o.fn.offset=function(){if(!this[0]){return{top:0,left:0}}if(this[0]===this[0].ownerDocument.body){return o.offset.bodyOffset(this[0])}o.offset.initialized||o.offset.initialize();var J=this[0],G=J.offsetParent,F=J,O=J.ownerDocument,M,H=O.documentElement,K=O.body,L=O.defaultView,E=L.getComputedStyle(J,null),N=J.offsetTop,I=J.offsetLeft;while((J=J.parentNode)&&J!==K&&J!==H){M=L.getComputedStyle(J,null);N-=J.scrollTop,I-=J.scrollLeft;if(J===G){N+=J.offsetTop,I+=J.offsetLeft;if(o.offset.doesNotAddBorder&&!(o.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(J.tagName))){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}F=G,G=J.offsetParent}if(o.offset.subtractsBorderForOverflowNotVisible&&M.overflow!=="visible"){N+=parseInt(M.borderTopWidth,10)||0,I+=parseInt(M.borderLeftWidth,10)||0}E=M}if(E.position==="relative"||E.position==="static"){N+=K.offsetTop,I+=K.offsetLeft}if(E.position==="fixed"){N+=Math.max(H.scrollTop,K.scrollTop),I+=Math.max(H.scrollLeft,K.scrollLeft)}return{top:N,left:I}}}o.offset={initialize:function(){if(this.initialized){return}var L=document.body,F=document.createElement("div"),H,G,N,I,M,E,J=L.style.marginTop,K='
';M={position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"};for(E in M){F.style[E]=M[E]}F.innerHTML=K;L.insertBefore(F,L.firstChild);H=F.firstChild,G=H.firstChild,I=H.nextSibling.firstChild.firstChild;this.doesNotAddBorder=(G.offsetTop!==5);this.doesAddBorderForTableAndCells=(I.offsetTop===5);H.style.overflow="hidden",H.style.position="relative";this.subtractsBorderForOverflowNotVisible=(G.offsetTop===-5);L.style.marginTop="1px";this.doesNotIncludeMarginInBodyOffset=(L.offsetTop===0);L.style.marginTop=J;L.removeChild(F);this.initialized=true},bodyOffset:function(E){o.offset.initialized||o.offset.initialize();var G=E.offsetTop,F=E.offsetLeft;if(o.offset.doesNotIncludeMarginInBodyOffset){G+=parseInt(o.curCSS(E,"marginTop",true),10)||0,F+=parseInt(o.curCSS(E,"marginLeft",true),10)||0}return{top:G,left:F}}};o.fn.extend({position:function(){var I=0,H=0,F;if(this[0]){var G=this.offsetParent(),J=this.offset(),E=/^body|html$/i.test(G[0].tagName)?{top:0,left:0}:G.offset();J.top-=j(this,"marginTop");J.left-=j(this,"marginLeft");E.top+=j(G,"borderTopWidth");E.left+=j(G,"borderLeftWidth");F={top:J.top-E.top,left:J.left-E.left}}return F},offsetParent:function(){var E=this[0].offsetParent||document.body;while(E&&(!/^body|html$/i.test(E.tagName)&&o.css(E,"position")=="static")){E=E.offsetParent}return o(E)}});o.each(["Left","Top"],function(F,E){var G="scroll"+E;o.fn[G]=function(H){if(!this[0]){return null}return H!==g?this.each(function(){this==l||this==document?l.scrollTo(!F?H:o(l).scrollLeft(),F?H:o(l).scrollTop()):this[G]=H}):this[0]==l||this[0]==document?self[F?"pageYOffset":"pageXOffset"]||o.boxModel&&document.documentElement[G]||document.body[G]:this[0][G]}});o.each(["Height","Width"],function(I,G){var E=I?"Left":"Top",H=I?"Right":"Bottom",F=G.toLowerCase();o.fn["inner"+G]=function(){return this[0]?o.css(this[0],F,false,"padding"):null};o.fn["outer"+G]=function(K){return this[0]?o.css(this[0],F,false,K?"margin":"border"):null};var J=G.toLowerCase();o.fn[J]=function(K){return this[0]==l?document.compatMode=="CSS1Compat"&&document.documentElement["client"+G]||document.body["client"+G]:this[0]==document?Math.max(document.documentElement["client"+G],document.body["scroll"+G],document.documentElement["scroll"+G],document.body["offset"+G],document.documentElement["offset"+G]):K===g?(this.length?o.css(this[0],J):null):this.css(J,typeof K==="string"?K:K+"px")}})})(); \ No newline at end of file diff --git a/js/istreamdev.js b/js/istreamdev.js index 10a465c..c27e99c 100644 --- a/js/istreamdev.js +++ b/js/istreamdev.js @@ -8,15 +8,15 @@ jQT = new $.jQTouch({ iconIsGlossy: true, fullscreen: true, preloadImages: [ - 'themes/jqt/img/chevron.png', - 'themes/jqt/img/back_button.png', - 'themes/jqt/img/back_button_clicked.png', - 'themes/jqt/img/button_clicked.png', - 'themes/jqt/img/button.png', - 'themes/jqt/img/button_clicked.png', - 'themes/jqt/img/loading.gif', - 'themes/jqt/img/toolbar.png', - 'themes/jqt/img/on_off.png', + 'img/chevron.png', + 'img/back_button.png', + 'img/back_button_clicked.png', + 'img/button_clicked.png', + 'img/button.png', + 'img/button_clicked.png', + 'img/loading.gif', + 'img/toolbar.png', + 'img/on_off.png', 'img/loading.gif', 'img/audio.png', 'img/epg.png', @@ -112,6 +112,7 @@ $('a[class="back"]').tap(function(event) { // show active sessions $(document).ready(function(e){ getRunningSessions(); +//preloadLogos(); }); //reinit RunningSessions when going to Home: @@ -139,6 +140,16 @@ $('#streaming').bind('pageAnimationStart', function(event, info){ } }) }); +//preload logos +function preloadLogos() { + $.getJSON("bin/genlogolist.php", + dataString, + function(data){ + for (var i = data.length - 1; i >= 0; i--) { + (new Image()).src = data[i]; + }; + }); +} // [/GENERIC STUFF] diff --git a/js/spinningwheel-min.js b/js/spinningwheel-min.js deleted file mode 100644 index a2b68dd..0000000 --- a/js/spinningwheel-min.js +++ /dev/null @@ -1 +0,0 @@ -var SpinningWheel={cellHeight:44,friction:0.003,slotData:[],handleEvent:function(e){if(e.type=="touchstart"){this.lockScreen(e);if(e.currentTarget.id=="sw-cancel"||e.currentTarget.id=="sw-done"){this.tapDown(e)}else{if(e.currentTarget.id=="sw-frame"){this.scrollStart(e)}}}else{if(e.type=="touchmove"){this.lockScreen(e);if(e.currentTarget.id=="sw-cancel"||e.currentTarget.id=="sw-done"){this.tapCancel(e)}else{if(e.currentTarget.id=="sw-frame"){this.scrollMove(e)}}}else{if(e.type=="touchend"){if(e.currentTarget.id=="sw-cancel"||e.currentTarget.id=="sw-done"){this.tapUp(e)}else{if(e.currentTarget.id=="sw-frame"){this.scrollEnd(e)}}}else{if(e.type=="webkitTransitionEnd"){if(e.target.id=="sw-wrapper"){this.destroy()}else{this.backWithinBoundaries(e)}}else{if(e.type=="orientationchange"){this.onOrientationChange(e)}else{if(e.type=="scroll"){this.onScroll(e)}}}}}}},onOrientationChange:function(e){window.scrollTo(0,0);this.swWrapper.style.top=window.innerHeight+window.pageYOffset+"px";this.calculateSlotsWidth()},onScroll:function(e){this.swWrapper.style.top=window.innerHeight+window.pageYOffset+"px"},lockScreen:function(e){e.preventDefault();e.stopPropagation()},reset:function(){this.slotEl=[];this.activeSlot=null;this.swWrapper=undefined;this.swSlotWrapper=undefined;this.swSlots=undefined;this.swFrame=undefined},calculateSlotsWidth:function(){var div=this.swSlots.getElementsByTagName("div");for(var i=0;i
Cancel
Done
';document.body.appendChild(div);this.swWrapper=div;this.swSlotWrapper=document.getElementById("sw-slots-wrapper");this.swSlots=document.getElementById("sw-slots");this.swFrame=document.getElementById("sw-frame");for(l=0;l"}ul.innerHTML=out;div=document.createElement("div");div.className=this.slotData[l].style;div.appendChild(ul);this.swSlots.appendChild(div);ul.slotPosition=l;ul.slotYPosition=0;ul.slotWidth=0;ul.slotMaxScroll=this.swSlotWrapper.clientHeight-ul.clientHeight-86;ul.style.webkitTransitionTimingFunction="cubic-bezier(0, 0, 0.2, 1)";this.slotEl.push(ul);if(this.slotData[l].defaultValue){this.scrollToValue(l,this.slotData[l].defaultValue)}}this.calculateSlotsWidth();document.addEventListener("touchstart",this,false);document.addEventListener("touchmove",this,false);window.addEventListener("orientationchange",this,true);window.addEventListener("scroll",this,true);document.getElementById("sw-cancel").addEventListener("touchstart",this,false);document.getElementById("sw-done").addEventListener("touchstart",this,false);this.swFrame.addEventListener("touchstart",this,false)},open:function(){this.create();this.swWrapper.style.webkitTransitionTimingFunction="ease-out";this.swWrapper.style.webkitTransitionDuration="400ms";this.swWrapper.style.webkitTransform="translate3d(0, -260px, 0)"},destroy:function(){this.swWrapper.removeEventListener("webkitTransitionEnd",this,false);this.swFrame.removeEventListener("touchstart",this,false);document.getElementById("sw-cancel").removeEventListener("touchstart",this,false);document.getElementById("sw-done").removeEventListener("touchstart",this,false);document.removeEventListener("touchstart",this,false);document.removeEventListener("touchmove",this,false);window.removeEventListener("orientationchange",this,true);window.removeEventListener("scroll",this,true);this.slotData=[];this.cancelAction=function(){return false};this.cancelDone=function(){return true};this.reset();document.body.removeChild(document.getElementById("sw-wrapper"))},close:function(){this.swWrapper.style.webkitTransitionTimingFunction="ease-in";this.swWrapper.style.webkitTransitionDuration="400ms";this.swWrapper.style.webkitTransform="translate3d(0, 0, 0)";this.swWrapper.addEventListener("webkitTransitionEnd",this,false)},addSlot:function(values,style,defaultValue){if(!style){style=""}style=style.split(" ");for(var i=0;i0){this.setPosition(i,0)}else{if(this.slotEl[i].slotYPosition0||this.slotEl[this.activeSlot].slotYPosition80){this.scrollStartY=this.slotEl[this.activeSlot].slotYPosition;this.scrollStartTime=e.timeStamp}},scrollEnd:function(e){this.swFrame.removeEventListener("touchmove",this,false);this.swFrame.removeEventListener("touchend",this,false);if(this.slotEl[this.activeSlot].slotYPosition>0||this.slotEl[this.activeSlot].slotYPosition0?0:this.slotEl[this.activeSlot].slotMaxScroll);return false}var scrollDistance=this.slotEl[this.activeSlot].slotYPosition-this.scrollStartY;if(scrollDistance-this.cellHeight/1.5){if(this.slotEl[this.activeSlot].slotYPosition%this.cellHeight){this.scrollTo(this.activeSlot,Math.round(this.slotEl[this.activeSlot].slotYPosition/this.cellHeight)*this.cellHeight,"100ms")}return false}var scrollDuration=e.timeStamp-this.scrollStartTime;var newDuration=(2*scrollDistance/scrollDuration)/this.friction;var newScrollDistance=(this.friction/2)*(newDuration*newDuration);if(newDuration<0){newDuration=-newDuration;newScrollDistance=-newScrollDistance}var newPosition=this.slotEl[this.activeSlot].slotYPosition+newScrollDistance;if(newPosition>0){newPosition/=2;newDuration/=3;if(newPosition>this.swSlotWrapper.clientHeight/4){newPosition=this.swSlotWrapper.clientHeight/4}}else{if(newPosition0||this.slotEl[slotNum].slotYPosition0?0:e.target.slotMaxScroll,"150ms");return false},tapDown:function(e){e.currentTarget.addEventListener("touchmove",this,false);e.currentTarget.addEventListener("touchend",this,false);e.currentTarget.className="sw-pressed"},tapCancel:function(e){e.currentTarget.removeEventListener("touchmove",this,false);e.currentTarget.removeEventListener("touchend",this,false);e.currentTarget.className=""},tapUp:function(e){this.tapCancel(e);if(e.currentTarget.id=="sw-cancel"){this.cancelAction()}else{this.doneAction()}this.close()},setCancelAction:function(action){this.cancelAction=action},setDoneAction:function(action){this.doneAction=action},cancelAction:function(){return false},cancelDone:function(){return true}}; \ No newline at end of file diff --git a/min/.htaccess b/min/.htaccess new file mode 100644 index 0000000..42f13eb --- /dev/null +++ b/min/.htaccess @@ -0,0 +1,4 @@ + +RewriteEngine on +RewriteRule ^([a-z]=.*) index.php?$1 [L,NE] + \ No newline at end of file diff --git a/min/config.php b/min/config.php new file mode 100644 index 0000000..b96749a --- /dev/null +++ b/min/config.php @@ -0,0 +1,156 @@ + + * array('//symlink' => '/real/target/path') // unix + * array('//static' => 'D:\\staticStorage') // Windows + * + */ +$min_symlinks = array(); + + +/** + * If you upload files from Windows to a non-Windows server, Windows may report + * incorrect mtimes for the files. This may cause Minify to keep serving stale + * cache files when source file changes are made too frequently (e.g. more than + * once an hour). + * + * Immediately after modifying and uploading a file, use the touch command to + * update the mtime on the server. If the mtime jumps ahead by a number of hours, + * set this variable to that number. If the mtime moves back, this should not be + * needed. + * + * In the Windows SFTP client WinSCP, there's an option that may fix this + * issue without changing the variable below. Under login > environment, + * select the option "Adjust remote timestamp with DST". + * @link http://winscp.net/eng/docs/ui_login_environment#daylight_saving_time + */ +$min_uploaderHoursBehind = 0; + + +/** + * Path to Minify's lib folder. If you happen to move it, change + * this accordingly. + */ +$min_libPath = dirname(__FILE__) . '/lib'; + + +// try to disable output_compression (may not have an effect) +ini_set('zlib.output_compression', '0'); diff --git a/min/groupsConfig.php b/min/groupsConfig.php new file mode 100644 index 0000000..9e2514d --- /dev/null +++ b/min/groupsConfig.php @@ -0,0 +1,34 @@ + array('//js/file1.js', '//js/file2.js'), + // 'css' => array('//css/file1.css', '//css/file2.css'), + + // custom source example + /*'js2' => array( + dirname(__FILE__) . '/../min_unit_tests/_test_files/js/before.js', + // do NOT process this file + new Minify_Source(array( + 'filepath' => dirname(__FILE__) . '/../min_unit_tests/_test_files/js/before.js', + 'minifier' => create_function('$a', 'return $a;') + )) + ),//*/ + + /*'js3' => array( + dirname(__FILE__) . '/../min_unit_tests/_test_files/js/before.js', + // do NOT process this file + new Minify_Source(array( + 'filepath' => dirname(__FILE__) . '/../min_unit_tests/_test_files/js/before.js', + 'minifier' => array('Minify_Packer', 'minify') + )) + ),//*/ +); \ No newline at end of file diff --git a/min/index.php b/min/index.php new file mode 100644 index 0000000..51c3525 --- /dev/null +++ b/min/index.php @@ -0,0 +1,66 @@ + + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ + + +/** + * Sends the given data to the FirePHP Firefox Extension. + * The data can be displayed in the Firebug Console or in the + * "Server" request tab. + * + * For more information see: http://www.firephp.org/ + * + * @copyright Copyright (C) 2007-2008 Christoph Dorn + * @author Christoph Dorn + * @license http://www.opensource.org/licenses/bsd-license.php + * @package FirePHP + */ +class FirePHP { + + /** + * FirePHP version + * + * @var string + */ + const VERSION = '0.2.0'; + + /** + * Firebug LOG level + * + * Logs a message to firebug console. + * + * @var string + */ + const LOG = 'LOG'; + + /** + * Firebug INFO level + * + * Logs a message to firebug console and displays an info icon before the message. + * + * @var string + */ + const INFO = 'INFO'; + + /** + * Firebug WARN level + * + * Logs a message to firebug console, displays an warning icon before the message and colors the line turquoise. + * + * @var string + */ + const WARN = 'WARN'; + + /** + * Firebug ERROR level + * + * Logs a message to firebug console, displays an error icon before the message and colors the line yellow. Also increments the firebug error count. + * + * @var string + */ + const ERROR = 'ERROR'; + + /** + * Dumps a variable to firebug's server panel + * + * @var string + */ + const DUMP = 'DUMP'; + + /** + * Displays a stack trace in firebug console + * + * @var string + */ + const TRACE = 'TRACE'; + + /** + * Displays an exception in firebug console + * + * Increments the firebug error count. + * + * @var string + */ + const EXCEPTION = 'EXCEPTION'; + + /** + * Displays an table in firebug console + * + * @var string + */ + const TABLE = 'TABLE'; + + /** + * Starts a group in firebug console + * + * @var string + */ + const GROUP_START = 'GROUP_START'; + + /** + * Ends a group in firebug console + * + * @var string + */ + const GROUP_END = 'GROUP_END'; + + /** + * Singleton instance of FirePHP + * + * @var FirePHP + */ + protected static $instance = null; + + /** + * Wildfire protocol message index + * + * @var int + */ + protected $messageIndex = 1; + + /** + * Options for the library + * + * @var array + */ + protected $options = array(); + + /** + * Filters used to exclude object members when encoding + * + * @var array + */ + protected $objectFilters = array(); + + /** + * A stack of objects used to detect recursion during object encoding + * + * @var object + */ + protected $objectStack = array(); + + /** + * Flag to enable/disable logging + * + * @var boolean + */ + protected $enabled = true; + + /** + * The object constructor + */ + function __construct() { + $this->options['maxObjectDepth'] = 10; + $this->options['maxArrayDepth'] = 20; + $this->options['useNativeJsonEncode'] = true; + $this->options['includeLineNumbers'] = true; + } + + /** + * When the object gets serialized only include specific object members. + * + * @return array + */ + public function __sleep() { + return array('options','objectFilters','enabled'); + } + + /** + * Gets singleton instance of FirePHP + * + * @param boolean $AutoCreate + * @return FirePHP + */ + public static function getInstance($AutoCreate=false) { + if($AutoCreate===true && !self::$instance) { + self::init(); + } + return self::$instance; + } + + /** + * Creates FirePHP object and stores it for singleton access + * + * @return FirePHP + */ + public static function init() { + return self::$instance = new self(); + } + + /** + * Enable and disable logging to Firebug + * + * @param boolean $Enabled TRUE to enable, FALSE to disable + * @return void + */ + public function setEnabled($Enabled) { + $this->enabled = $Enabled; + } + + /** + * Check if logging is enabled + * + * @return boolean TRUE if enabled + */ + public function getEnabled() { + return $this->enabled; + } + + /** + * Specify a filter to be used when encoding an object + * + * Filters are used to exclude object members. + * + * @param string $Class The class name of the object + * @param array $Filter An array or members to exclude + * @return void + */ + public function setObjectFilter($Class, $Filter) { + $this->objectFilters[$Class] = $Filter; + } + + /** + * Set some options for the library + * + * Options: + * - maxObjectDepth: The maximum depth to traverse objects (default: 10) + * - maxArrayDepth: The maximum depth to traverse arrays (default: 20) + * - useNativeJsonEncode: If true will use json_encode() (default: true) + * - includeLineNumbers: If true will include line numbers and filenames (default: true) + * + * @param array $Options The options to be set + * @return void + */ + public function setOptions($Options) { + $this->options = array_merge($this->options,$Options); + } + + /** + * Register FirePHP as your error handler + * + * Will throw exceptions for each php error. + */ + public function registerErrorHandler() + { + //NOTE: The following errors will not be caught by this error handler: + // E_ERROR, E_PARSE, E_CORE_ERROR, + // E_CORE_WARNING, E_COMPILE_ERROR, + // E_COMPILE_WARNING, E_STRICT + + set_error_handler(array($this,'errorHandler')); + } + + /** + * FirePHP's error handler + * + * Throws exception for each php error that will occur. + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $errcontext + */ + public function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) + { + // Don't throw exception if error reporting is switched off + if (error_reporting() == 0) { + return; + } + // Only throw exceptions for errors we are asking for + if (error_reporting() & $errno) { + throw new ErrorException($errstr, 0, $errno, $errfile, $errline); + } + } + + /** + * Register FirePHP as your exception handler + */ + public function registerExceptionHandler() + { + set_exception_handler(array($this,'exceptionHandler')); + } + + /** + * FirePHP's exception handler + * + * Logs all exceptions to your firebug console and then stops the script. + * + * @param Exception $Exception + * @throws Exception + */ + function exceptionHandler($Exception) { + $this->fb($Exception); + } + + /** + * Set custom processor url for FirePHP + * + * @param string $URL + */ + public function setProcessorUrl($URL) + { + $this->setHeader('X-FirePHP-ProcessorURL', $URL); + } + + /** + * Set custom renderer url for FirePHP + * + * @param string $URL + */ + public function setRendererUrl($URL) + { + $this->setHeader('X-FirePHP-RendererURL', $URL); + } + + /** + * Start a group for following messages + * + * @param string $Name + * @return true + * @throws Exception + */ + public function group($Name) { + return $this->fb(null, $Name, FirePHP::GROUP_START); + } + + /** + * Ends a group you have started before + * + * @return true + * @throws Exception + */ + public function groupEnd() { + return $this->fb(null, null, FirePHP::GROUP_END); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::LOG + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function log($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::LOG); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::INFO + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function info($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::INFO); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::WARN + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function warn($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::WARN); + } + + /** + * Log object with label to firebug console + * + * @see FirePHP::ERROR + * @param mixes $Object + * @param string $Label + * @return true + * @throws Exception + */ + public function error($Object, $Label=null) { + return $this->fb($Object, $Label, FirePHP::ERROR); + } + + /** + * Dumps key and variable to firebug server panel + * + * @see FirePHP::DUMP + * @param string $Key + * @param mixed $Variable + * @return true + * @throws Exception + */ + public function dump($Key, $Variable) { + return $this->fb($Variable, $Key, FirePHP::DUMP); + } + + /** + * Log a trace in the firebug console + * + * @see FirePHP::TRACE + * @param string $Label + * @return true + * @throws Exception + */ + public function trace($Label) { + return $this->fb($Label, FirePHP::TRACE); + } + + /** + * Log a table in the firebug console + * + * @see FirePHP::TABLE + * @param string $Label + * @param string $Table + * @return true + * @throws Exception + */ + public function table($Label, $Table) { + return $this->fb($Table, $Label, FirePHP::TABLE); + } + + /** + * Check if FirePHP is installed on client + * + * @return boolean + */ + public function detectClientExtension() { + /* Check if FirePHP is installed on client */ + if(!@preg_match_all('/\sFirePHP\/([\.|\d]*)\s?/si',$this->getUserAgent(),$m) || + !version_compare($m[1][0],'0.0.6','>=')) { + return false; + } + return true; + } + + /** + * Log varible to Firebug + * + * @see http://www.firephp.org/Wiki/Reference/Fb + * @param mixed $Object The variable to be logged + * @return true Return TRUE if message was added to headers, FALSE otherwise + * @throws Exception + */ + public function fb($Object) { + + if(!$this->enabled) { + return false; + } + + if (headers_sent($filename, $linenum)) { + throw $this->newException('Headers already sent in '.$filename.' on line '.$linenum.'. Cannot send log data to FirePHP. You must have Output Buffering enabled via ob_start() or output_buffering ini directive.'); + } + + $Type = null; + $Label = null; + + if(func_num_args()==1) { + } else + if(func_num_args()==2) { + switch(func_get_arg(1)) { + case self::LOG: + case self::INFO: + case self::WARN: + case self::ERROR: + case self::DUMP: + case self::TRACE: + case self::EXCEPTION: + case self::TABLE: + case self::GROUP_START: + case self::GROUP_END: + $Type = func_get_arg(1); + break; + default: + $Label = func_get_arg(1); + break; + } + } else + if(func_num_args()==3) { + $Type = func_get_arg(2); + $Label = func_get_arg(1); + } else { + throw $this->newException('Wrong number of arguments to fb() function!'); + } + + + if(!$this->detectClientExtension()) { + return false; + } + + $meta = array(); + $skipFinalObjectEncode = false; + + if($Object instanceof Exception) { + + $meta['file'] = $this->_escapeTraceFile($Object->getFile()); + $meta['line'] = $Object->getLine(); + + $trace = $Object->getTrace(); + if($Object instanceof ErrorException + && isset($trace[0]['function']) + && $trace[0]['function']=='errorHandler' + && isset($trace[0]['class']) + && $trace[0]['class']=='FirePHP') { + + $severity = false; + switch($Object->getSeverity()) { + case E_WARNING: $severity = 'E_WARNING'; break; + case E_NOTICE: $severity = 'E_NOTICE'; break; + case E_USER_ERROR: $severity = 'E_USER_ERROR'; break; + case E_USER_WARNING: $severity = 'E_USER_WARNING'; break; + case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break; + case E_STRICT: $severity = 'E_STRICT'; break; + case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break; + case E_DEPRECATED: $severity = 'E_DEPRECATED'; break; + case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break; + } + + $Object = array('Class'=>get_class($Object), + 'Message'=>$severity.': '.$Object->getMessage(), + 'File'=>$this->_escapeTraceFile($Object->getFile()), + 'Line'=>$Object->getLine(), + 'Type'=>'trigger', + 'Trace'=>$this->_escapeTrace(array_splice($trace,2))); + $skipFinalObjectEncode = true; + } else { + $Object = array('Class'=>get_class($Object), + 'Message'=>$Object->getMessage(), + 'File'=>$this->_escapeTraceFile($Object->getFile()), + 'Line'=>$Object->getLine(), + 'Type'=>'throw', + 'Trace'=>$this->_escapeTrace($trace)); + $skipFinalObjectEncode = true; + } + $Type = self::EXCEPTION; + + } else + if($Type==self::TRACE) { + + $trace = debug_backtrace(); + if(!$trace) return false; + for( $i=0 ; $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if($trace[$i]['function']=='fb' + || $trace[$i]['function']=='trace' + || $trace[$i]['function']=='send') { + $Object = array('Class'=>isset($trace[$i]['class'])?$trace[$i]['class']:'', + 'Type'=>isset($trace[$i]['type'])?$trace[$i]['type']:'', + 'Function'=>isset($trace[$i]['function'])?$trace[$i]['function']:'', + 'Message'=>$trace[$i]['args'][0], + 'File'=>isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):'', + 'Line'=>isset($trace[$i]['line'])?$trace[$i]['line']:'', + 'Args'=>isset($trace[$i]['args'])?$this->encodeObject($trace[$i]['args']):'', + 'Trace'=>$this->_escapeTrace(array_splice($trace,$i+1))); + + $skipFinalObjectEncode = true; + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } else + if($Type==self::TABLE) { + + if(isset($Object[0]) && is_string($Object[0])) { + $Object[1] = $this->encodeTable($Object[1]); + } else { + $Object = $this->encodeTable($Object); + } + + $skipFinalObjectEncode = true; + + } else { + if($Type===null) { + $Type = self::LOG; + } + } + + if($this->options['includeLineNumbers']) { + if(!isset($meta['file']) || !isset($meta['line'])) { + + $trace = debug_backtrace(); + for( $i=0 ; $trace && $i_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php' + || substr($this->_standardizePath($trace[$i]['file']),-29,29)=='FirePHPCore/FirePHP.class.php')) { + /* Skip - FB::trace(), FB::send(), $firephp->trace(), $firephp->fb() */ + } else + if(isset($trace[$i]['class']) + && isset($trace[$i+1]['file']) + && $trace[$i]['class']=='FirePHP' + && substr($this->_standardizePath($trace[$i+1]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip fb() */ + } else + if(isset($trace[$i]['file']) + && substr($this->_standardizePath($trace[$i]['file']),-18,18)=='FirePHPCore/fb.php') { + /* Skip FB::fb() */ + } else { + $meta['file'] = isset($trace[$i]['file'])?$this->_escapeTraceFile($trace[$i]['file']):''; + $meta['line'] = isset($trace[$i]['line'])?$trace[$i]['line']:''; + break; + } + } + + } + } else { + unset($meta['file']); + unset($meta['line']); + } + + $this->setHeader('X-Wf-Protocol-1','http://meta.wildfirehq.org/Protocol/JsonStream/0.2'); + $this->setHeader('X-Wf-1-Plugin-1','http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/'.self::VERSION); + + $structure_index = 1; + if($Type==self::DUMP) { + $structure_index = 2; + $this->setHeader('X-Wf-1-Structure-2','http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1'); + } else { + $this->setHeader('X-Wf-1-Structure-1','http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'); + } + + if($Type==self::DUMP) { + $msg = '{"'.$Label.'":'.$this->jsonEncode($Object, $skipFinalObjectEncode).'}'; + } else { + $msg_meta = array('Type'=>$Type); + if($Label!==null) { + $msg_meta['Label'] = $Label; + } + if(isset($meta['file'])) { + $msg_meta['File'] = $meta['file']; + } + if(isset($meta['line'])) { + $msg_meta['Line'] = $meta['line']; + } + $msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode($Object, $skipFinalObjectEncode).']'; + } + + $parts = explode("\n",chunk_split($msg, 5000, "\n")); + + for( $i=0 ; $i2) { + // Message needs to be split into multiple parts + $this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + (($i==0)?strlen($msg):'') + . '|' . $part . '|' + . (($isetHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex, + strlen($part) . '|' . $part . '|'); + } + + $this->messageIndex++; + + if ($this->messageIndex > 99999) { + throw new Exception('Maximum number (99,999) of messages reached!'); + } + } + } + + $this->setHeader('X-Wf-1-Index',$this->messageIndex-1); + + return true; + } + + /** + * Standardizes path for windows systems. + * + * @param string $Path + * @return string + */ + protected function _standardizePath($Path) { + return preg_replace('/\\\\+/','/',$Path); + } + + /** + * Escape trace path for windows systems + * + * @param array $Trace + * @return array + */ + protected function _escapeTrace($Trace) { + if(!$Trace) return $Trace; + for( $i=0 ; $i_escapeTraceFile($Trace[$i]['file']); + } + if(isset($Trace[$i]['args'])) { + $Trace[$i]['args'] = $this->encodeObject($Trace[$i]['args']); + } + } + return $Trace; + } + + /** + * Escape file information of trace for windows systems + * + * @param string $File + * @return string + */ + protected function _escapeTraceFile($File) { + /* Check if we have a windows filepath */ + if(strpos($File,'\\')) { + /* First strip down to single \ */ + + $file = preg_replace('/\\\\+/','\\',$File); + + return $file; + } + return $File; + } + + /** + * Send header + * + * @param string $Name + * @param string_type $Value + */ + protected function setHeader($Name, $Value) { + return header($Name.': '.$Value); + } + + /** + * Get user agent + * + * @return string|false + */ + protected function getUserAgent() { + if(!isset($_SERVER['HTTP_USER_AGENT'])) return false; + return $_SERVER['HTTP_USER_AGENT']; + } + + /** + * Returns a new exception + * + * @param string $Message + * @return Exception + */ + protected function newException($Message) { + return new Exception($Message); + } + + /** + * Encode an object into a JSON string + * + * Uses PHP's jeson_encode() if available + * + * @param object $Object The object to be encoded + * @return string The JSON string + */ + protected function jsonEncode($Object, $skipObjectEncode=false) + { + if(!$skipObjectEncode) { + $Object = $this->encodeObject($Object); + } + + if(function_exists('json_encode') + && $this->options['useNativeJsonEncode']!=false) { + + return json_encode($Object); + } else { + return $this->json_encode($Object); + } + } + + /** + * Encodes a table by encoding each row and column with encodeObject() + * + * @param array $Table The table to be encoded + * @return array + */ + protected function encodeTable($Table) { + if(!$Table) return $Table; + for( $i=0 ; $iencodeObject($Table[$i][$j]); + } + } + } + return $Table; + } + + /** + * Encodes an object including members with + * protected and private visibility + * + * @param Object $Object The object to be encoded + * @param int $Depth The current traversal depth + * @return array All members of the object + */ + protected function encodeObject($Object, $ObjectDepth = 1, $ArrayDepth = 1) + { + $return = array(); + + if (is_object($Object)) { + + if ($ObjectDepth > $this->options['maxObjectDepth']) { + return '** Max Object Depth ('.$this->options['maxObjectDepth'].') **'; + } + + foreach ($this->objectStack as $refVal) { + if ($refVal === $Object) { + return '** Recursion ('.get_class($Object).') **'; + } + } + array_push($this->objectStack, $Object); + + $return['__className'] = $class = get_class($Object); + + $reflectionClass = new ReflectionClass($class); + $properties = array(); + foreach( $reflectionClass->getProperties() as $property) { + $properties[$property->getName()] = $property; + } + + $members = (array)$Object; + + foreach( $properties as $raw_name => $property ) { + + $name = $raw_name; + if($property->isStatic()) { + $name = 'static:'.$name; + } + if($property->isPublic()) { + $name = 'public:'.$name; + } else + if($property->isPrivate()) { + $name = 'private:'.$name; + $raw_name = "\0".$class."\0".$raw_name; + } else + if($property->isProtected()) { + $name = 'protected:'.$name; + $raw_name = "\0".'*'."\0".$raw_name; + } + + if(!(isset($this->objectFilters[$class]) + && is_array($this->objectFilters[$class]) + && in_array($raw_name,$this->objectFilters[$class]))) { + + if(array_key_exists($raw_name,$members) + && !$property->isStatic()) { + + $return[$name] = $this->encodeObject($members[$raw_name], $ObjectDepth + 1, 1); + + } else { + if(method_exists($property,'setAccessible')) { + $property->setAccessible(true); + $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1); + } else + if($property->isPublic()) { + $return[$name] = $this->encodeObject($property->getValue($Object), $ObjectDepth + 1, 1); + } else { + $return[$name] = '** Need PHP 5.3 to get value **'; + } + } + } else { + $return[$name] = '** Excluded by Filter **'; + } + } + + // Include all members that are not defined in the class + // but exist in the object + foreach( $members as $raw_name => $value ) { + + $name = $raw_name; + + if ($name{0} == "\0") { + $parts = explode("\0", $name); + $name = $parts[2]; + } + + if(!isset($properties[$name])) { + $name = 'undeclared:'.$name; + + if(!(isset($this->objectFilters[$class]) + && is_array($this->objectFilters[$class]) + && in_array($raw_name,$this->objectFilters[$class]))) { + + $return[$name] = $this->encodeObject($value, $ObjectDepth + 1, 1); + } else { + $return[$name] = '** Excluded by Filter **'; + } + } + } + + array_pop($this->objectStack); + + } elseif (is_array($Object)) { + + if ($ArrayDepth > $this->options['maxArrayDepth']) { + return '** Max Array Depth ('.$this->options['maxArrayDepth'].') **'; + } + + foreach ($Object as $key => $val) { + + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($key=='GLOBALS' + && is_array($val) + && array_key_exists('GLOBALS',$val)) { + $val['GLOBALS'] = '** Recursion (GLOBALS) **'; + } + + $return[$key] = $this->encodeObject($val, 1, $ArrayDepth + 1); + } + } else { + if(self::is_utf8($Object)) { + return $Object; + } else { + return utf8_encode($Object); + } + } + return $return; + } + + /** + * Returns true if $string is valid UTF-8 and false otherwise. + * + * @param mixed $str String to be tested + * @return boolean + */ + protected static function is_utf8($str) { + $c=0; $b=0; + $bits=0; + $len=strlen($str); + for($i=0; $i<$len; $i++){ + $c=ord($str[$i]); + if($c > 128){ + if(($c >= 254)) return false; + elseif($c >= 252) $bits=6; + elseif($c >= 248) $bits=5; + elseif($c >= 240) $bits=4; + elseif($c >= 224) $bits=3; + elseif($c >= 192) $bits=2; + else return false; + if(($i+$bits) > $len) return false; + while($bits > 1){ + $i++; + $b=ord($str[$i]); + if($b < 128 || $b > 191) return false; + $bits--; + } + } + } + return true; + } + + /** + * Converts to and from JSON format. + * + * JSON (JavaScript Object Notation) is a lightweight data-interchange + * format. It is easy for humans to read and write. It is easy for machines + * to parse and generate. It is based on a subset of the JavaScript + * Programming Language, Standard ECMA-262 3rd Edition - December 1999. + * This feature can also be found in Python. JSON is a text format that is + * completely language independent but uses conventions that are familiar + * to programmers of the C-family of languages, including C, C++, C#, Java, + * JavaScript, Perl, TCL, and many others. These properties make JSON an + * ideal data-interchange language. + * + * This package provides a simple encoder and decoder for JSON notation. It + * is intended for use with client-side Javascript applications that make + * use of HTTPRequest to perform server communication functions - data can + * be encoded into JSON notation for use in a client-side javascript, or + * decoded from incoming Javascript requests. JSON format is native to + * Javascript, and can be directly eval()'ed with no further parsing + * overhead + * + * All strings should be in ASCII or UTF-8 format! + * + * LICENSE: Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: Redistributions of source code must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * @category + * @package Services_JSON + * @author Michal Migurski + * @author Matt Knapp + * @author Brett Stimmerman + * @author Christoph Dorn + * @copyright 2005 Michal Migurski + * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + + + /** + * Keep a list of objects as we descend into the array so we can detect recursion. + */ + private $json_objectStack = array(); + + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + private function json_utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + private function json_encode($var) + { + + if(is_object($var)) { + if(in_array($var,$this->json_objectStack)) { + return '"** Recursion **"'; + } + } + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->json_utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($var), + array_values($var)); + + array_pop($this->json_objectStack); + + foreach($properties as $property) { + if($property instanceof Exception) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + $this->json_objectStack[] = $var; + + // treat it like a regular array + $elements = array_map(array($this, 'json_encode'), $var); + + array_pop($this->json_objectStack); + + foreach($elements as $element) { + if($element instanceof Exception) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = self::encodeObject($var); + + $this->json_objectStack[] = $var; + + $properties = array_map(array($this, 'json_name_value'), + array_keys($vars), + array_values($vars)); + + array_pop($this->json_objectStack); + + foreach($properties as $property) { + if($property instanceof Exception) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return null; + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + private function json_name_value($name, $value) + { + // Encoding the $GLOBALS PHP array causes an infinite loop + // if the recursion is not reset here as it contains + // a reference to itself. This is the only way I have come up + // with to stop infinite recursion in this case. + if($name=='GLOBALS' + && is_array($value) + && array_key_exists('GLOBALS',$value)) { + $value['GLOBALS'] = '** Recursion **'; + } + + $encoded_value = $this->json_encode($value); + + if($encoded_value instanceof Exception) { + return $encoded_value; + } + + return $this->json_encode(strval($name)) . ':' . $encoded_value; + } +} diff --git a/min/lib/HTTP/ConditionalGet.php b/min/lib/HTTP/ConditionalGet.php new file mode 100644 index 0000000..823db05 --- /dev/null +++ b/min/lib/HTTP/ConditionalGet.php @@ -0,0 +1,348 @@ + + * list($updateTime, $content) = getDbUpdateAndContent(); + * $cg = new HTTP_ConditionalGet(array( + * 'lastModifiedTime' => $updateTime + * ,'isPublic' => true + * )); + * $cg->sendHeaders(); + * if ($cg->cacheIsValid) { + * exit(); + * } + * echo $content; + * + * + * E.g. Shortcut for the above + * + * HTTP_ConditionalGet::check($updateTime, true); // exits if client has cache + * echo $content; + * + * + * E.g. Content from DB with no update time: + * + * $content = getContentFromDB(); + * $cg = new HTTP_ConditionalGet(array( + * 'contentHash' => md5($content) + * )); + * $cg->sendHeaders(); + * if ($cg->cacheIsValid) { + * exit(); + * } + * echo $content; + * + * + * E.g. Static content with some static includes: + * + * // before content + * $cg = new HTTP_ConditionalGet(array( + * 'lastUpdateTime' => max( + * filemtime(__FILE__) + * ,filemtime('/path/to/header.inc') + * ,filemtime('/path/to/footer.inc') + * ) + * )); + * $cg->sendHeaders(); + * if ($cg->cacheIsValid) { + * exit(); + * } + * + * @package Minify + * @subpackage HTTP + * @author Stephen Clay + */ +class HTTP_ConditionalGet { + + /** + * Does the client have a valid copy of the requested resource? + * + * You'll want to check this after instantiating the object. If true, do + * not send content, just call sendHeaders() if you haven't already. + * + * @var bool + */ + public $cacheIsValid = null; + + /** + * @param array $spec options + * + * 'isPublic': (bool) if true, the Cache-Control header will contain + * "public", allowing proxies to cache the content. Otherwise "private" will + * be sent, allowing only browser caching. (default false) + * + * 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers + * will be sent with content. This is recommended. + * + * 'encoding': (string) if set, the header "Vary: Accept-Encoding" will + * always be sent and a truncated version of the encoding will be appended + * to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient + * checking of the client's If-None-Match header, as the encoding portion of + * the ETag will be stripped before comparison. + * + * 'contentHash': (string) if given, only the ETag header can be sent with + * content (only HTTP1.1 clients can conditionally GET). The given string + * should be short with no quote characters and always change when the + * resource changes (recommend md5()). This is not needed/used if + * lastModifiedTime is given. + * + * 'eTag': (string) if given, this will be used as the ETag header rather + * than values based on lastModifiedTime or contentHash. Also the encoding + * string will not be appended to the given value as described above. + * + * 'invalidate': (bool) if true, the client cache will be considered invalid + * without testing. Effectively this disables conditional GET. + * (default false) + * + * 'maxAge': (int) if given, this will set the Cache-Control max-age in + * seconds, and also set the Expires header to the equivalent GMT date. + * After the max-age period has passed, the browser will again send a + * conditional GET to revalidate its cache. + * + * @return null + */ + public function __construct($spec) + { + $scope = (isset($spec['isPublic']) && $spec['isPublic']) + ? 'public' + : 'private'; + $maxAge = 0; + // backwards compatibility (can be removed later) + if (isset($spec['setExpires']) + && is_numeric($spec['setExpires']) + && ! isset($spec['maxAge'])) { + $spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME']; + } + if (isset($spec['maxAge'])) { + $maxAge = $spec['maxAge']; + $this->_headers['Expires'] = self::gmtDate( + $_SERVER['REQUEST_TIME'] + $spec['maxAge'] + ); + } + $etagAppend = ''; + if (isset($spec['encoding'])) { + $this->_stripEtag = true; + $this->_headers['Vary'] = 'Accept-Encoding'; + if ('' !== $spec['encoding']) { + if (0 === strpos($spec['encoding'], 'x-')) { + $spec['encoding'] = substr($spec['encoding'], 2); + } + $etagAppend = ';' . substr($spec['encoding'], 0, 2); + } + } + if (isset($spec['lastModifiedTime'])) { + $this->_setLastModified($spec['lastModifiedTime']); + if (isset($spec['eTag'])) { // Use it + $this->_setEtag($spec['eTag'], $scope); + } else { // base both headers on time + $this->_setEtag($spec['lastModifiedTime'] . $etagAppend, $scope); + } + } elseif (isset($spec['eTag'])) { // Use it + $this->_setEtag($spec['eTag'], $scope); + } elseif (isset($spec['contentHash'])) { // Use the hash as the ETag + $this->_setEtag($spec['contentHash'] . $etagAppend, $scope); + } + $this->_headers['Cache-Control'] = "max-age={$maxAge}, {$scope}"; + // invalidate cache if disabled, otherwise check + $this->cacheIsValid = (isset($spec['invalidate']) && $spec['invalidate']) + ? false + : $this->_isCacheValid(); + } + + /** + * Get array of output headers to be sent + * + * In the case of 304 responses, this array will only contain the response + * code header: array('_responseCode' => 'HTTP/1.0 304 Not Modified') + * + * Otherwise something like: + * + * array( + * 'Cache-Control' => 'max-age=0, public' + * ,'ETag' => '"foobar"' + * ) + * + * + * @return array + */ + public function getHeaders() + { + return $this->_headers; + } + + /** + * Set the Content-Length header in bytes + * + * With most PHP configs, as long as you don't flush() output, this method + * is not needed and PHP will buffer all output and set Content-Length for + * you. Otherwise you'll want to call this to let the client know up front. + * + * @param int $bytes + * + * @return int copy of input $bytes + */ + public function setContentLength($bytes) + { + return $this->_headers['Content-Length'] = $bytes; + } + + /** + * Send headers + * + * @see getHeaders() + * + * Note this doesn't "clear" the headers. Calling sendHeaders() will + * call header() again (but probably have not effect) and getHeaders() will + * still return the headers. + * + * @return null + */ + public function sendHeaders() + { + $headers = $this->_headers; + if (array_key_exists('_responseCode', $headers)) { + header($headers['_responseCode']); + unset($headers['_responseCode']); + } + foreach ($headers as $name => $val) { + header($name . ': ' . $val); + } + } + + /** + * Exit if the client's cache is valid for this resource + * + * This is a convenience method for common use of the class + * + * @param int $lastModifiedTime if given, both ETag AND Last-Modified headers + * will be sent with content. This is recommended. + * + * @param bool $isPublic (default false) if true, the Cache-Control header + * will contain "public", allowing proxies to cache the content. Otherwise + * "private" will be sent, allowing only browser caching. + * + * @param array $options (default empty) additional options for constructor + * + * @return null + */ + public static function check($lastModifiedTime = null, $isPublic = false, $options = array()) + { + if (null !== $lastModifiedTime) { + $options['lastModifiedTime'] = (int)$lastModifiedTime; + } + $options['isPublic'] = (bool)$isPublic; + $cg = new HTTP_ConditionalGet($options); + $cg->sendHeaders(); + if ($cg->cacheIsValid) { + exit(); + } + } + + + /** + * Get a GMT formatted date for use in HTTP headers + * + * + * header('Expires: ' . HTTP_ConditionalGet::gmtdate($time)); + * + * + * @param int $time unix timestamp + * + * @return string + */ + public static function gmtDate($time) + { + return gmdate('D, d M Y H:i:s \G\M\T', $time); + } + + protected $_headers = array(); + protected $_lmTime = null; + protected $_etag = null; + protected $_stripEtag = false; + + protected function _setEtag($hash, $scope) + { + $this->_etag = '"' . substr($scope, 0, 3) . $hash . '"'; + $this->_headers['ETag'] = $this->_etag; + } + + protected function _setLastModified($time) + { + $this->_lmTime = (int)$time; + $this->_headers['Last-Modified'] = self::gmtDate($time); + } + + /** + * Determine validity of client cache and queue 304 header if valid + */ + protected function _isCacheValid() + { + if (null === $this->_etag) { + // lmTime is copied to ETag, so this condition implies that the + // server sent neither ETag nor Last-Modified, so the client can't + // possibly has a valid cache. + return false; + } + $isValid = ($this->resourceMatchedEtag() || $this->resourceNotModified()); + if ($isValid) { + $this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified'; + } + return $isValid; + } + + protected function resourceMatchedEtag() + { + if (!isset($_SERVER['HTTP_IF_NONE_MATCH'])) { + return false; + } + $clientEtagList = get_magic_quotes_gpc() + ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) + : $_SERVER['HTTP_IF_NONE_MATCH']; + $clientEtags = explode(',', $clientEtagList); + + $compareTo = $this->normalizeEtag($this->_etag); + foreach ($clientEtags as $clientEtag) { + if ($this->normalizeEtag($clientEtag) === $compareTo) { + // respond with the client's matched ETag, even if it's not what + // we would've sent by default + $this->_headers['ETag'] = trim($clientEtag); + return true; + } + } + return false; + } + + protected function normalizeEtag($etag) { + $etag = trim($etag); + return $this->_stripEtag + ? preg_replace('/;\\w\\w"$/', '"', $etag) + : $etag; + } + + protected function resourceNotModified() + { + if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { + return false; + } + $ifModifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE']; + if (false !== ($semicolon = strrpos($ifModifiedSince, ';'))) { + // IE has tacked on extra data to this header, strip it + $ifModifiedSince = substr($ifModifiedSince, 0, $semicolon); + } + if ($ifModifiedSince == self::gmtDate($this->_lmTime)) { + // Apache 2.2's behavior. If there was no ETag match, send the + // non-encoded version of the ETag value. + $this->_headers['ETag'] = $this->normalizeEtag($this->_etag); + return true; + } + return false; + } +} diff --git a/min/lib/HTTP/Encoder.php b/min/lib/HTTP/Encoder.php new file mode 100644 index 0000000..05ca552 --- /dev/null +++ b/min/lib/HTTP/Encoder.php @@ -0,0 +1,326 @@ + + * // Send a CSS file, compressed if possible + * $he = new HTTP_Encoder(array( + * 'content' => file_get_contents($cssFile) + * ,'type' => 'text/css' + * )); + * $he->encode(); + * $he->sendAll(); + * + * + * + * // Shortcut to encoding output + * header('Content-Type: text/css'); // needed if not HTML + * HTTP_Encoder::output($css); + * + * + * + * // Just sniff for the accepted encoding + * $encoding = HTTP_Encoder::getAcceptedEncoding(); + * + * + * For more control over headers, use getHeaders() and getData() and send your + * own output. + * + * Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate, + * and gzcompress functions for gzip, deflate, and compress-encoding + * respectively. + * + * @package Minify + * @subpackage HTTP + * @author Stephen Clay + */ +class HTTP_Encoder { + + /** + * Should the encoder allow HTTP encoding to IE6? + * + * If you have many IE6 users and the bandwidth savings is worth troubling + * some of them, set this to true. + * + * By default, encoding is only offered to IE7+. When this is true, + * getAcceptedEncoding() will return an encoding for IE6 if its user agent + * string contains "SV1". This has been documented in many places as "safe", + * but there seem to be remaining, intermittent encoding bugs in patched + * IE6 on the wild web. + * + * @var bool + */ + public static $encodeToIe6 = false; + + + /** + * Default compression level for zlib operations + * + * This level is used if encode() is not given a $compressionLevel + * + * @var int + */ + public static $compressionLevel = 6; + + + /** + * Get an HTTP Encoder object + * + * @param array $spec options + * + * 'content': (string required) content to be encoded + * + * 'type': (string) if set, the Content-Type header will have this value. + * + * 'method: (string) only set this if you are forcing a particular encoding + * method. If not set, the best method will be chosen by getAcceptedEncoding() + * The available methods are 'gzip', 'deflate', 'compress', and '' (no + * encoding) + * + * @return null + */ + public function __construct($spec) + { + $this->_content = $spec['content']; + $this->_headers['Content-Length'] = (string)strlen($this->_content); + if (isset($spec['type'])) { + $this->_headers['Content-Type'] = $spec['type']; + } + if (isset($spec['method']) + && in_array($spec['method'], array('gzip', 'deflate', 'compress', ''))) + { + $this->_encodeMethod = array($spec['method'], $spec['method']); + } else { + $this->_encodeMethod = self::getAcceptedEncoding(); + } + } + + /** + * Get content in current form + * + * Call after encode() for encoded content. + * + * return string + */ + public function getContent() + { + return $this->_content; + } + + /** + * Get array of output headers to be sent + * + * E.g. + * + * array( + * 'Content-Length' => '615' + * ,'Content-Encoding' => 'x-gzip' + * ,'Vary' => 'Accept-Encoding' + * ) + * + * + * @return array + */ + public function getHeaders() + { + return $this->_headers; + } + + /** + * Send output headers + * + * You must call this before headers are sent and it probably cannot be + * used in conjunction with zlib output buffering / mod_gzip. Errors are + * not handled purposefully. + * + * @see getHeaders() + * + * @return null + */ + public function sendHeaders() + { + foreach ($this->_headers as $name => $val) { + header($name . ': ' . $val); + } + } + + /** + * Send output headers and content + * + * A shortcut for sendHeaders() and echo getContent() + * + * You must call this before headers are sent and it probably cannot be + * used in conjunction with zlib output buffering / mod_gzip. Errors are + * not handled purposefully. + * + * @return null + */ + public function sendAll() + { + $this->sendHeaders(); + echo $this->_content; + } + + /** + * Determine the client's best encoding method from the HTTP Accept-Encoding + * header. + * + * If no Accept-Encoding header is set, or the browser is IE before v6 SP2, + * this will return ('', ''), the "identity" encoding. + * + * A syntax-aware scan is done of the Accept-Encoding, so the method must + * be non 0. The methods are favored in order of gzip, deflate, then + * compress. Deflate is always smallest and generally faster, but is + * rarely sent by servers, so client support could be buggier. + * + * @param bool $allowCompress allow the older compress encoding + * + * @param bool $allowDeflate allow the more recent deflate encoding + * + * @return array two values, 1st is the actual encoding method, 2nd is the + * alias of that method to use in the Content-Encoding header (some browsers + * call gzip "x-gzip" etc.) + */ + public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true) + { + // @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + + if (! isset($_SERVER['HTTP_ACCEPT_ENCODING']) + || self::_isBuggyIe()) + { + return array('', ''); + } + $ae = $_SERVER['HTTP_ACCEPT_ENCODING']; + // gzip checks (quick) + if (0 === strpos($ae, 'gzip,') // most browsers + || 0 === strpos($ae, 'deflate, gzip,') // opera + ) { + return array('gzip', 'gzip'); + } + // gzip checks (slow) + if (preg_match( + '@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@' + ,$ae + ,$m)) { + return array('gzip', $m[1]); + } + if ($allowDeflate) { + // deflate checks + $aeRev = strrev($ae); + if (0 === strpos($aeRev, 'etalfed ,') // ie, webkit + || 0 === strpos($aeRev, 'etalfed,') // gecko + || 0 === strpos($ae, 'deflate,') // opera + // slow parsing + || preg_match( + '@(?:^|,)\\s*deflate\\s*(?:$|,|;\\s*q=(?:0\\.|1))@', $ae)) { + return array('deflate', 'deflate'); + } + } + if ($allowCompress && preg_match( + '@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@' + ,$ae + ,$m)) { + return array('compress', $m[1]); + } + return array('', ''); + } + + /** + * Encode (compress) the content + * + * If the encode method is '' (none) or compression level is 0, or the 'zlib' + * extension isn't loaded, we return false. + * + * Then the appropriate gz_* function is called to compress the content. If + * this fails, false is returned. + * + * The header "Vary: Accept-Encoding" is added. If encoding is successful, + * the Content-Length header is updated, and Content-Encoding is also added. + * + * @param int $compressionLevel given to zlib functions. If not given, the + * class default will be used. + * + * @return bool success true if the content was actually compressed + */ + public function encode($compressionLevel = null) + { + $this->_headers['Vary'] = 'Accept-Encoding'; + if (null === $compressionLevel) { + $compressionLevel = self::$compressionLevel; + } + if ('' === $this->_encodeMethod[0] + || ($compressionLevel == 0) + || !extension_loaded('zlib')) + { + return false; + } + if ($this->_encodeMethod[0] === 'deflate') { + $encoded = gzdeflate($this->_content, $compressionLevel); + } elseif ($this->_encodeMethod[0] === 'gzip') { + $encoded = gzencode($this->_content, $compressionLevel); + } else { + $encoded = gzcompress($this->_content, $compressionLevel); + } + if (false === $encoded) { + return false; + } + $this->_headers['Content-Length'] = strlen($encoded); + $this->_headers['Content-Encoding'] = $this->_encodeMethod[1]; + $this->_content = $encoded; + return true; + } + + /** + * Encode and send appropriate headers and content + * + * This is a convenience method for common use of the class + * + * @param string $content + * + * @param int $compressionLevel given to zlib functions. If not given, the + * class default will be used. + * + * @return bool success true if the content was actually compressed + */ + public static function output($content, $compressionLevel = null) + { + if (null === $compressionLevel) { + $compressionLevel = self::$compressionLevel; + } + $he = new HTTP_Encoder(array('content' => $content)); + $ret = $he->encode($compressionLevel); + $he->sendAll(); + return $ret; + } + + protected $_content = ''; + protected $_headers = array(); + protected $_encodeMethod = array('', ''); + + /** + * Is the browser an IE version earlier than 6 SP2? + */ + protected static function _isBuggyIe() + { + $ua = $_SERVER['HTTP_USER_AGENT']; + // quick escape for non-IEs + if (0 !== strpos($ua, 'Mozilla/4.0 (compatible; MSIE ') + || false !== strpos($ua, 'Opera')) { + return false; + } + // no regex = faaast + $version = (float)substr($ua, 30); + return self::$encodeToIe6 + ? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1'))) + : ($version < 7); + } +} diff --git a/min/lib/JSMin.php b/min/lib/JSMin.php new file mode 100644 index 0000000..770e1c6 --- /dev/null +++ b/min/lib/JSMin.php @@ -0,0 +1,314 @@ + (PHP port) + * @author Steve Clay (modifications + cleanup) + * @author Andrea Giammarchi (spaceBeforeRegExp) + * @copyright 2002 Douglas Crockford (jsmin.c) + * @copyright 2008 Ryan Grove (PHP port) + * @license http://opensource.org/licenses/mit-license.php MIT License + * @link http://code.google.com/p/jsmin-php/ + */ + +class JSMin { + const ORD_LF = 10; + const ORD_SPACE = 32; + const ACTION_KEEP_A = 1; + const ACTION_DELETE_A = 2; + const ACTION_DELETE_A_B = 3; + + protected $a = "\n"; + protected $b = ''; + protected $input = ''; + protected $inputIndex = 0; + protected $inputLength = 0; + protected $lookAhead = null; + protected $output = ''; + + /** + * Minify Javascript + * + * @param string $js Javascript to be minified + * @return string + */ + public static function minify($js) + { + $jsmin = new JSMin($js); + return $jsmin->min(); + } + + /** + * Setup process + */ + public function __construct($input) + { + $this->input = str_replace("\r\n", "\n", $input); + $this->inputLength = strlen($this->input); + } + + /** + * Perform minification, return result + */ + public function min() + { + if ($this->output !== '') { // min already run + return $this->output; + } + $this->action(self::ACTION_DELETE_A_B); + + while ($this->a !== null) { + // determine next command + $command = self::ACTION_KEEP_A; // default + if ($this->a === ' ') { + if (! $this->isAlphaNum($this->b)) { + $command = self::ACTION_DELETE_A; + } + } elseif ($this->a === "\n") { + if ($this->b === ' ') { + $command = self::ACTION_DELETE_A_B; + } elseif (false === strpos('{[(+-', $this->b) + && ! $this->isAlphaNum($this->b)) { + $command = self::ACTION_DELETE_A; + } + } elseif (! $this->isAlphaNum($this->a)) { + if ($this->b === ' ' + || ($this->b === "\n" + && (false === strpos('}])+-"\'', $this->a)))) { + $command = self::ACTION_DELETE_A_B; + } + } + $this->action($command); + } + $this->output = trim($this->output); + return $this->output; + } + + /** + * ACTION_KEEP_A = Output A. Copy B to A. Get the next B. + * ACTION_DELETE_A = Copy B to A. Get the next B. + * ACTION_DELETE_A_B = Get the next B. + */ + protected function action($command) + { + switch ($command) { + case self::ACTION_KEEP_A: + $this->output .= $this->a; + // fallthrough + case self::ACTION_DELETE_A: + $this->a = $this->b; + if ($this->a === "'" || $this->a === '"') { // string literal + $str = $this->a; // in case needed for exception + while (true) { + $this->output .= $this->a; + $this->a = $this->get(); + if ($this->a === $this->b) { // end quote + break; + } + if (ord($this->a) <= self::ORD_LF) { + throw new JSMin_UnterminatedStringException( + 'Unterminated String: ' . var_export($str, true)); + } + $str .= $this->a; + if ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + $str .= $this->a; + } + } + } + // fallthrough + case self::ACTION_DELETE_A_B: + $this->b = $this->next(); + if ($this->b === '/' && $this->isRegexpLiteral()) { // RegExp literal + $this->output .= $this->a . $this->b; + $pattern = '/'; // in case needed for exception + while (true) { + $this->a = $this->get(); + $pattern .= $this->a; + if ($this->a === '/') { // end pattern + break; // while (true) + } elseif ($this->a === '\\') { + $this->output .= $this->a; + $this->a = $this->get(); + $pattern .= $this->a; + } elseif (ord($this->a) <= self::ORD_LF) { + throw new JSMin_UnterminatedRegExpException( + 'Unterminated RegExp: '. var_export($pattern, true)); + } + $this->output .= $this->a; + } + $this->b = $this->next(); + } + // end case ACTION_DELETE_A_B + } + } + + protected function isRegexpLiteral() + { + if (false !== strpos("\n{;(,=:[!&|?", $this->a)) { // we aren't dividing + return true; + } + if (' ' === $this->a) { + $length = strlen($this->output); + if ($length < 2) { // weird edge case + return true; + } + // you can't divide a keyword + if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) { + if ($this->output === $m[0]) { // odd but could happen + return true; + } + // make sure it's a keyword, not end of an identifier + $charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1); + if (! $this->isAlphaNum($charBeforeKeyword)) { + return true; + } + } + } + return false; + } + + /** + * Get next char. Convert ctrl char to space. + */ + protected function get() + { + $c = $this->lookAhead; + $this->lookAhead = null; + if ($c === null) { + if ($this->inputIndex < $this->inputLength) { + $c = $this->input[$this->inputIndex]; + $this->inputIndex += 1; + } else { + return null; + } + } + if ($c === "\r" || $c === "\n") { + return "\n"; + } + if (ord($c) < self::ORD_SPACE) { // control char + return ' '; + } + return $c; + } + + /** + * Get next char. If is ctrl character, translate to a space or newline. + */ + protected function peek() + { + $this->lookAhead = $this->get(); + return $this->lookAhead; + } + + /** + * Is $c a letter, digit, underscore, dollar sign, escape, or non-ASCII? + */ + protected function isAlphaNum($c) + { + return (preg_match('/^[0-9a-zA-Z_\\$\\\\]$/', $c) || ord($c) > 126); + } + + protected function singleLineComment() + { + $comment = ''; + while (true) { + $get = $this->get(); + $comment .= $get; + if (ord($get) <= self::ORD_LF) { // EOL reached + // if IE conditional comment + if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) { + return "/{$comment}"; + } + return $get; + } + } + } + + protected function multipleLineComment() + { + $this->get(); + $comment = ''; + while (true) { + $get = $this->get(); + if ($get === '*') { + if ($this->peek() === '/') { // end of comment reached + $this->get(); + // if comment preserved by YUI Compressor + if (0 === strpos($comment, '!')) { + return "\n/*" . substr($comment, 1) . "*/\n"; + } + // if IE conditional comment + if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) { + return "/*{$comment}*/"; + } + return ' '; + } + } elseif ($get === null) { + throw new JSMin_UnterminatedCommentException('Unterminated Comment: ' . var_export('/*' . $comment, true)); + } + $comment .= $get; + } + } + + /** + * Get the next character, skipping over comments. + * Some comments may be preserved. + */ + protected function next() + { + $get = $this->get(); + if ($get !== '/') { + return $get; + } + switch ($this->peek()) { + case '/': return $this->singleLineComment(); + case '*': return $this->multipleLineComment(); + default: return $get; + } + } +} + +class JSMin_UnterminatedStringException extends Exception {} +class JSMin_UnterminatedCommentException extends Exception {} +class JSMin_UnterminatedRegExpException extends Exception {} diff --git a/min/lib/JSMinPlus.php b/min/lib/JSMinPlus.php new file mode 100644 index 0000000..31a1a5c --- /dev/null +++ b/min/lib/JSMinPlus.php @@ -0,0 +1,1872 @@ + + * + * Usage: $minified = JSMinPlus::minify($script [, $filename]) + * + * Versionlog (see also changelog.txt): + * 12-04-2009 - some small bugfixes and performance improvements + * 09-04-2009 - initial open sourced version 1.0 + * + * Latest version of this script: http://files.tweakers.net/jsminplus/jsminplus.zip + * + */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Narcissus JavaScript engine. + * + * The Initial Developer of the Original Code is + * Brendan Eich . + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): Tino Zijdel + * PHP port, modifications and minifier routine are (C) 2009 + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define('TOKEN_END', 1); +define('TOKEN_NUMBER', 2); +define('TOKEN_IDENTIFIER', 3); +define('TOKEN_STRING', 4); +define('TOKEN_REGEXP', 5); +define('TOKEN_NEWLINE', 6); +define('TOKEN_CONDCOMMENT_MULTILINE', 7); + +define('JS_SCRIPT', 100); +define('JS_BLOCK', 101); +define('JS_LABEL', 102); +define('JS_FOR_IN', 103); +define('JS_CALL', 104); +define('JS_NEW_WITH_ARGS', 105); +define('JS_INDEX', 106); +define('JS_ARRAY_INIT', 107); +define('JS_OBJECT_INIT', 108); +define('JS_PROPERTY_INIT', 109); +define('JS_GETTER', 110); +define('JS_SETTER', 111); +define('JS_GROUP', 112); +define('JS_LIST', 113); + +define('DECLARED_FORM', 0); +define('EXPRESSED_FORM', 1); +define('STATEMENT_FORM', 2); + +class JSMinPlus +{ + private $parser; + private $reserved = array( + 'break', 'case', 'catch', 'continue', 'default', 'delete', 'do', + 'else', 'finally', 'for', 'function', 'if', 'in', 'instanceof', + 'new', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'var', + 'void', 'while', 'with', + // Words reserved for future use + 'abstract', 'boolean', 'byte', 'char', 'class', 'const', 'debugger', + 'double', 'enum', 'export', 'extends', 'final', 'float', 'goto', + 'implements', 'import', 'int', 'interface', 'long', 'native', + 'package', 'private', 'protected', 'public', 'short', 'static', + 'super', 'synchronized', 'throws', 'transient', 'volatile', + // These are not reserved, but should be taken into account + // in isValidIdentifier (See jslint source code) + 'arguments', 'eval', 'true', 'false', 'Infinity', 'NaN', 'null', 'undefined' + ); + + private function __construct() + { + $this->parser = new JSParser(); + } + + public static function minify($js, $filename='') + { + static $instance; + + // this is a singleton + if(!$instance) + $instance = new JSMinPlus(); + + return $instance->min($js, $filename); + } + + private function min($js, $filename) + { + try + { + $n = $this->parser->parse($js, $filename, 1); + return $this->parseTree($n); + } + catch(Exception $e) + { + echo $e->getMessage() . "\n"; + } + + return false; + } + + private function parseTree($n, $noBlockGrouping = false) + { + $s = ''; + + switch ($n->type) + { + case KEYWORD_FUNCTION: + $s .= 'function' . ($n->name ? ' ' . $n->name : '') . '('; + $params = $n->params; + for ($i = 0, $j = count($params); $i < $j; $i++) + $s .= ($i ? ',' : '') . $params[$i]; + $s .= '){' . $this->parseTree($n->body, true) . '}'; + break; + + case JS_SCRIPT: + // we do nothing with funDecls or varDecls + $noBlockGrouping = true; + // fall through + case JS_BLOCK: + $childs = $n->treeNodes; + for ($c = 0, $i = 0, $j = count($childs); $i < $j; $i++) + { + $t = $this->parseTree($childs[$i]); + if (strlen($t)) + { + if ($c) + { + if ($childs[$i]->type == KEYWORD_FUNCTION && $childs[$i]->functionForm == DECLARED_FORM) + $s .= "\n"; // put declared functions on a new line + else + $s .= ';'; + } + + $s .= $t; + + $c++; + } + } + + if ($c > 1 && !$noBlockGrouping) + { + $s = '{' . $s . '}'; + } + break; + + case KEYWORD_IF: + $s = 'if(' . $this->parseTree($n->condition) . ')'; + $thenPart = $this->parseTree($n->thenPart); + $elsePart = $n->elsePart ? $this->parseTree($n->elsePart) : null; + + // quite a rancid hack to see if we should enclose the thenpart in brackets + if ($thenPart[0] != '{') + { + if (strpos($thenPart, 'if(') !== false) + $thenPart = '{' . $thenPart . '}'; + elseif ($elsePart) + $thenPart .= ';'; + } + + $s .= $thenPart; + + if ($elsePart) + { + $s .= 'else'; + + if ($elsePart[0] != '{') + $s .= ' '; + + $s .= $elsePart; + } + break; + + case KEYWORD_SWITCH: + $s = 'switch(' . $this->parseTree($n->discriminant) . '){'; + $cases = $n->cases; + for ($i = 0, $j = count($cases); $i < $j; $i++) + { + $case = $cases[$i]; + if ($case->type == KEYWORD_CASE) + $s .= 'case' . ($case->caseLabel->type != TOKEN_STRING ? ' ' : '') . $this->parseTree($case->caseLabel) . ':'; + else + $s .= 'default:'; + + $statement = $this->parseTree($case->statements); + if ($statement) + $s .= $statement . ';'; + } + $s = rtrim($s, ';') . '}'; + break; + + case KEYWORD_FOR: + $s = 'for(' . ($n->setup ? $this->parseTree($n->setup) : '') + . ';' . ($n->condition ? $this->parseTree($n->condition) : '') + . ';' . ($n->update ? $this->parseTree($n->update) : '') . ')' + . $this->parseTree($n->body); + break; + + case KEYWORD_WHILE: + $s = 'while(' . $this->parseTree($n->condition) . ')' . $this->parseTree($n->body); + break; + + case JS_FOR_IN: + $s = 'for(' . ($n->varDecl ? $this->parseTree($n->varDecl) : $this->parseTree($n->iterator)) . ' in ' . $this->parseTree($n->object) . ')' . $this->parseTree($n->body); + break; + + case KEYWORD_DO: + $s = 'do{' . $this->parseTree($n->body, true) . '}while(' . $this->parseTree($n->condition) . ')'; + break; + + case KEYWORD_BREAK: + case KEYWORD_CONTINUE: + $s = $n->value . ($n->label ? ' ' . $n->label : ''); + break; + + case KEYWORD_TRY: + $s = 'try{' . $this->parseTree($n->tryBlock, true) . '}'; + $catchClauses = $n->catchClauses; + for ($i = 0, $j = count($catchClauses); $i < $j; $i++) + { + $t = $catchClauses[$i]; + $s .= 'catch(' . $t->varName . ($t->guard ? ' if ' . $this->parseTree($t->guard) : '') . '){' . $this->parseTree($t->block, true) . '}'; + } + if ($n->finallyBlock) + $s .= 'finally{' . $this->parseTree($n->finallyBlock, true) . '}'; + break; + + case KEYWORD_THROW: + $s = 'throw ' . $this->parseTree($n->exception); + break; + + case KEYWORD_RETURN: + $s = 'return' . ($n->value ? ' ' . $this->parseTree($n->value) : ''); + break; + + case KEYWORD_WITH: + $s = 'with(' . $this->parseTree($n->object) . ')' . $this->parseTree($n->body); + break; + + case KEYWORD_VAR: + case KEYWORD_CONST: + $s = $n->value . ' '; + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + { + $t = $childs[$i]; + $s .= ($i ? ',' : '') . $t->name; + $u = $t->initializer; + if ($u) + $s .= '=' . $this->parseTree($u); + } + break; + + case KEYWORD_DEBUGGER: + throw new Exception('NOT IMPLEMENTED: DEBUGGER'); + break; + + case TOKEN_CONDCOMMENT_MULTILINE: + $s = $n->value . ' '; + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + $s .= $this->parseTree($childs[$i]); + break; + + case OP_SEMICOLON: + if ($expression = $n->expression) + $s = $this->parseTree($expression); + break; + + case JS_LABEL: + $s = $n->label . ':' . $this->parseTree($n->statement); + break; + + case OP_COMMA: + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + $s .= ($i ? ',' : '') . $this->parseTree($childs[$i]); + break; + + case OP_ASSIGN: + $s = $this->parseTree($n->treeNodes[0]) . $n->value . $this->parseTree($n->treeNodes[1]); + break; + + case OP_HOOK: + $s = $this->parseTree($n->treeNodes[0]) . '?' . $this->parseTree($n->treeNodes[1]) . ':' . $this->parseTree($n->treeNodes[2]); + break; + + case OP_OR: case OP_AND: + case OP_BITWISE_OR: case OP_BITWISE_XOR: case OP_BITWISE_AND: + case OP_EQ: case OP_NE: case OP_STRICT_EQ: case OP_STRICT_NE: + case OP_LT: case OP_LE: case OP_GE: case OP_GT: + case OP_LSH: case OP_RSH: case OP_URSH: + case OP_MUL: case OP_DIV: case OP_MOD: + $s = $this->parseTree($n->treeNodes[0]) . $n->type . $this->parseTree($n->treeNodes[1]); + break; + + case OP_PLUS: + case OP_MINUS: + $s = $this->parseTree($n->treeNodes[0]) . $n->type; + $nextTokenType = $n->treeNodes[1]->type; + if ( $nextTokenType == OP_PLUS || $nextTokenType == OP_MINUS || + $nextTokenType == OP_INCREMENT || $nextTokenType == OP_DECREMENT || + $nextTokenType == OP_UNARY_PLUS || $nextTokenType == OP_UNARY_MINUS + ) + $s .= ' '; + $s .= $this->parseTree($n->treeNodes[1]); + break; + + case KEYWORD_IN: + $s = $this->parseTree($n->treeNodes[0]) . ' in ' . $this->parseTree($n->treeNodes[1]); + break; + + case KEYWORD_INSTANCEOF: + $s = $this->parseTree($n->treeNodes[0]) . ' instanceof ' . $this->parseTree($n->treeNodes[1]); + break; + + case KEYWORD_DELETE: + $s = 'delete ' . $this->parseTree($n->treeNodes[0]); + break; + + case KEYWORD_VOID: + $s = 'void(' . $this->parseTree($n->treeNodes[0]) . ')'; + break; + + case KEYWORD_TYPEOF: + $s = 'typeof ' . $this->parseTree($n->treeNodes[0]); + break; + + case OP_NOT: + case OP_BITWISE_NOT: + case OP_UNARY_PLUS: + case OP_UNARY_MINUS: + $s = $n->value . $this->parseTree($n->treeNodes[0]); + break; + + case OP_INCREMENT: + case OP_DECREMENT: + if ($n->postfix) + $s = $this->parseTree($n->treeNodes[0]) . $n->value; + else + $s = $n->value . $this->parseTree($n->treeNodes[0]); + break; + + case OP_DOT: + $s = $this->parseTree($n->treeNodes[0]) . '.' . $this->parseTree($n->treeNodes[1]); + break; + + case JS_INDEX: + $s = $this->parseTree($n->treeNodes[0]); + // See if we can replace named index with a dot saving 3 bytes + if ( $n->treeNodes[0]->type == TOKEN_IDENTIFIER && + $n->treeNodes[1]->type == TOKEN_STRING && + $this->isValidIdentifier(substr($n->treeNodes[1]->value, 1, -1)) + ) + $s .= '.' . substr($n->treeNodes[1]->value, 1, -1); + else + $s .= '[' . $this->parseTree($n->treeNodes[1]) . ']'; + break; + + case JS_LIST: + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + $s .= ($i ? ',' : '') . $this->parseTree($childs[$i]); + break; + + case JS_CALL: + $s = $this->parseTree($n->treeNodes[0]) . '(' . $this->parseTree($n->treeNodes[1]) . ')'; + break; + + case KEYWORD_NEW: + case JS_NEW_WITH_ARGS: + $s = 'new ' . $this->parseTree($n->treeNodes[0]) . '(' . ($n->type == JS_NEW_WITH_ARGS ? $this->parseTree($n->treeNodes[1]) : '') . ')'; + break; + + case JS_ARRAY_INIT: + $s = '['; + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + { + $s .= ($i ? ',' : '') . $this->parseTree($childs[$i]); + } + $s .= ']'; + break; + + case JS_OBJECT_INIT: + $s = '{'; + $childs = $n->treeNodes; + for ($i = 0, $j = count($childs); $i < $j; $i++) + { + $t = $childs[$i]; + if ($i) + $s .= ','; + if ($t->type == JS_PROPERTY_INIT) + { + // Ditch the quotes when the index is a valid identifier + if ( $t->treeNodes[0]->type == TOKEN_STRING && + $this->isValidIdentifier(substr($t->treeNodes[0]->value, 1, -1)) + ) + $s .= substr($t->treeNodes[0]->value, 1, -1); + else + $s .= $t->treeNodes[0]->value; + + $s .= ':' . $this->parseTree($t->treeNodes[1]); + } + else + { + $s .= $t->type == JS_GETTER ? 'get' : 'set'; + $s .= ' ' . $t->name . '('; + $params = $t->params; + for ($i = 0, $j = count($params); $i < $j; $i++) + $s .= ($i ? ',' : '') . $params[$i]; + $s .= '){' . $this->parseTree($t->body, true) . '}'; + } + } + $s .= '}'; + break; + + case KEYWORD_NULL: case KEYWORD_THIS: case KEYWORD_TRUE: case KEYWORD_FALSE: + case TOKEN_IDENTIFIER: case TOKEN_NUMBER: case TOKEN_STRING: case TOKEN_REGEXP: + $s = $n->value; + break; + + case JS_GROUP: + $s = '(' . $this->parseTree($n->treeNodes[0]) . ')'; + break; + + default: + throw new Exception('UNKNOWN TOKEN TYPE: ' . $n->type); + } + + return $s; + } + + private function isValidIdentifier($string) + { + return preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $string) && !in_array($string, $this->reserved); + } +} + +class JSParser +{ + private $t; + + private $opPrecedence = array( + ';' => 0, + ',' => 1, + '=' => 2, '?' => 2, ':' => 2, + // The above all have to have the same precedence, see bug 330975. + '||' => 4, + '&&' => 5, + '|' => 6, + '^' => 7, + '&' => 8, + '==' => 9, '!=' => 9, '===' => 9, '!==' => 9, + '<' => 10, '<=' => 10, '>=' => 10, '>' => 10, 'in' => 10, 'instanceof' => 10, + '<<' => 11, '>>' => 11, '>>>' => 11, + '+' => 12, '-' => 12, + '*' => 13, '/' => 13, '%' => 13, + 'delete' => 14, 'void' => 14, 'typeof' => 14, + '!' => 14, '~' => 14, 'U+' => 14, 'U-' => 14, + '++' => 15, '--' => 15, + 'new' => 16, + '.' => 17, + JS_NEW_WITH_ARGS => 0, JS_INDEX => 0, JS_CALL => 0, + JS_ARRAY_INIT => 0, JS_OBJECT_INIT => 0, JS_GROUP => 0 + ); + + private $opArity = array( + ',' => -2, + '=' => 2, + '?' => 3, + '||' => 2, + '&&' => 2, + '|' => 2, + '^' => 2, + '&' => 2, + '==' => 2, '!=' => 2, '===' => 2, '!==' => 2, + '<' => 2, '<=' => 2, '>=' => 2, '>' => 2, 'in' => 2, 'instanceof' => 2, + '<<' => 2, '>>' => 2, '>>>' => 2, + '+' => 2, '-' => 2, + '*' => 2, '/' => 2, '%' => 2, + 'delete' => 1, 'void' => 1, 'typeof' => 1, + '!' => 1, '~' => 1, 'U+' => 1, 'U-' => 1, + '++' => 1, '--' => 1, + 'new' => 1, + '.' => 2, + JS_NEW_WITH_ARGS => 2, JS_INDEX => 2, JS_CALL => 2, + JS_ARRAY_INIT => 1, JS_OBJECT_INIT => 1, JS_GROUP => 1, + TOKEN_CONDCOMMENT_MULTILINE => 1 + ); + + public function __construct() + { + $this->t = new JSTokenizer(); + } + + public function parse($s, $f, $l) + { + // initialize tokenizer + $this->t->init($s, $f, $l); + + $x = new JSCompilerContext(false); + $n = $this->Script($x); + if (!$this->t->isDone()) + throw $this->t->newSyntaxError('Syntax error'); + + return $n; + } + + private function Script($x) + { + $n = $this->Statements($x); + $n->type = JS_SCRIPT; + $n->funDecls = $x->funDecls; + $n->varDecls = $x->varDecls; + + return $n; + } + + private function Statements($x) + { + $n = new JSNode($this->t, JS_BLOCK); + array_push($x->stmtStack, $n); + + while (!$this->t->isDone() && $this->t->peek() != OP_RIGHT_CURLY) + $n->addNode($this->Statement($x)); + + array_pop($x->stmtStack); + + return $n; + } + + private function Block($x) + { + $this->t->mustMatch(OP_LEFT_CURLY); + $n = $this->Statements($x); + $this->t->mustMatch(OP_RIGHT_CURLY); + + return $n; + } + + private function Statement($x) + { + $tt = $this->t->get(); + $n2 = null; + + // Cases for statements ending in a right curly return early, avoiding the + // common semicolon insertion magic after this switch. + switch ($tt) + { + case KEYWORD_FUNCTION: + return $this->FunctionDefinition( + $x, + true, + count($x->stmtStack) > 1 ? STATEMENT_FORM : DECLARED_FORM + ); + break; + + case OP_LEFT_CURLY: + $n = $this->Statements($x); + $this->t->mustMatch(OP_RIGHT_CURLY); + return $n; + + case KEYWORD_IF: + $n = new JSNode($this->t); + $n->condition = $this->ParenExpression($x); + array_push($x->stmtStack, $n); + $n->thenPart = $this->Statement($x); + $n->elsePart = $this->t->match(KEYWORD_ELSE) ? $this->Statement($x) : null; + array_pop($x->stmtStack); + return $n; + + case KEYWORD_SWITCH: + $n = new JSNode($this->t); + $this->t->mustMatch(OP_LEFT_PAREN); + $n->discriminant = $this->Expression($x); + $this->t->mustMatch(OP_RIGHT_PAREN); + $n->cases = array(); + $n->defaultIndex = -1; + + array_push($x->stmtStack, $n); + + $this->t->mustMatch(OP_LEFT_CURLY); + + while (($tt = $this->t->get()) != OP_RIGHT_CURLY) + { + switch ($tt) + { + case KEYWORD_DEFAULT: + if ($n->defaultIndex >= 0) + throw $this->t->newSyntaxError('More than one switch default'); + // FALL THROUGH + case KEYWORD_CASE: + $n2 = new JSNode($this->t); + if ($tt == KEYWORD_DEFAULT) + $n->defaultIndex = count($n->cases); + else + $n2->caseLabel = $this->Expression($x, OP_COLON); + break; + default: + throw $this->t->newSyntaxError('Invalid switch case'); + } + + $this->t->mustMatch(OP_COLON); + $n2->statements = new JSNode($this->t, JS_BLOCK); + while (($tt = $this->t->peek()) != KEYWORD_CASE && $tt != KEYWORD_DEFAULT && $tt != OP_RIGHT_CURLY) + $n2->statements->addNode($this->Statement($x)); + + array_push($n->cases, $n2); + } + + array_pop($x->stmtStack); + return $n; + + case KEYWORD_FOR: + $n = new JSNode($this->t); + $n->isLoop = true; + $this->t->mustMatch(OP_LEFT_PAREN); + + if (($tt = $this->t->peek()) != OP_SEMICOLON) + { + $x->inForLoopInit = true; + if ($tt == KEYWORD_VAR || $tt == KEYWORD_CONST) + { + $this->t->get(); + $n2 = $this->Variables($x); + } + else + { + $n2 = $this->Expression($x); + } + $x->inForLoopInit = false; + } + + if ($n2 && $this->t->match(KEYWORD_IN)) + { + $n->type = JS_FOR_IN; + if ($n2->type == KEYWORD_VAR) + { + if (count($n2->treeNodes) != 1) + { + throw $this->t->SyntaxError( + 'Invalid for..in left-hand side', + $this->t->filename, + $n2->lineno + ); + } + + // NB: n2[0].type == IDENTIFIER and n2[0].value == n2[0].name. + $n->iterator = $n2->treeNodes[0]; + $n->varDecl = $n2; + } + else + { + $n->iterator = $n2; + $n->varDecl = null; + } + + $n->object = $this->Expression($x); + } + else + { + $n->setup = $n2 ? $n2 : null; + $this->t->mustMatch(OP_SEMICOLON); + $n->condition = $this->t->peek() == OP_SEMICOLON ? null : $this->Expression($x); + $this->t->mustMatch(OP_SEMICOLON); + $n->update = $this->t->peek() == OP_RIGHT_PAREN ? null : $this->Expression($x); + } + + $this->t->mustMatch(OP_RIGHT_PAREN); + $n->body = $this->nest($x, $n); + return $n; + + case KEYWORD_WHILE: + $n = new JSNode($this->t); + $n->isLoop = true; + $n->condition = $this->ParenExpression($x); + $n->body = $this->nest($x, $n); + return $n; + + case KEYWORD_DO: + $n = new JSNode($this->t); + $n->isLoop = true; + $n->body = $this->nest($x, $n, KEYWORD_WHILE); + $n->condition = $this->ParenExpression($x); + if (!$x->ecmaStrictMode) + { + // "; + * $link = ""; + * + * // in min.php + * Minify::serve('Groups', array( + * 'groups' => $groupSources + * ,'setExpires' => (time() + 86400 * 365) + * )); + * + * + * @package Minify + * @author Stephen Clay + */ +class Minify_Build { + + /** + * Last modification time of all files in the build + * + * @var int + */ + public $lastModified = 0; + + /** + * String to use as ampersand in uri(). Set this to '&' if + * you are not HTML-escaping URIs. + * + * @var string + */ + public static $ampersand = '&'; + + /** + * Get a time-stamped URI + * + * + * echo $b->uri('/site.js'); + * // outputs "/site.js?1678242" + * + * echo $b->uri('/scriptaculous.js?load=effects'); + * // outputs "/scriptaculous.js?load=effects&1678242" + * + * + * @param string $uri + * @param boolean $forceAmpersand (default = false) Force the use of ampersand to + * append the timestamp to the URI. + * @return string + */ + public function uri($uri, $forceAmpersand = false) { + $sep = ($forceAmpersand || strpos($uri, '?') !== false) + ? self::$ampersand + : '?'; + return "{$uri}{$sep}{$this->lastModified}"; + } + + /** + * Create a build object + * + * @param array $sources array of Minify_Source objects and/or file paths + * + * @return null + */ + public function __construct($sources) + { + $max = 0; + foreach ((array)$sources as $source) { + if ($source instanceof Minify_Source) { + $max = max($max, $source->lastModified); + } elseif (is_string($source)) { + if (0 === strpos($source, '//')) { + $source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1); + } + if (is_file($source)) { + $max = max($max, filemtime($source)); + } + } + } + $this->lastModified = $max; + } +} diff --git a/min/lib/Minify/CSS.php b/min/lib/Minify/CSS.php new file mode 100644 index 0000000..b2d8e0b --- /dev/null +++ b/min/lib/Minify/CSS.php @@ -0,0 +1,83 @@ + + * @author http://code.google.com/u/1stvamp/ (Issue 64 patch) + */ +class Minify_CSS { + + /** + * Minify a CSS string + * + * @param string $css + * + * @param array $options available options: + * + * 'preserveComments': (default true) multi-line comments that begin + * with "/*!" will be preserved with newlines before and after to + * enhance readability. + * + * 'prependRelativePath': (default null) if given, this string will be + * prepended to all relative URIs in import/url declarations + * + * 'currentDir': (default null) if given, this is assumed to be the + * directory of the current CSS file. Using this, minify will rewrite + * all relative URIs in import/url declarations to correctly point to + * the desired files. For this to work, the files *must* exist and be + * visible by the PHP process. + * + * 'symlinks': (default = array()) If the CSS file is stored in + * a symlink-ed directory, provide an array of link paths to + * target paths, where the link paths are within the document root. Because + * paths need to be normalized for this to work, use "//" to substitute + * the doc root in the link paths (the array keys). E.g.: + * + * array('//symlink' => '/real/target/path') // unix + * array('//static' => 'D:\\staticStorage') // Windows + * + * + * @return string + */ + public static function minify($css, $options = array()) + { + require_once 'Minify/CSS/Compressor.php'; + if (isset($options['preserveComments']) + && !$options['preserveComments']) { + $css = Minify_CSS_Compressor::process($css, $options); + } else { + require_once 'Minify/CommentPreserver.php'; + $css = Minify_CommentPreserver::process( + $css + ,array('Minify_CSS_Compressor', 'process') + ,array($options) + ); + } + if (! isset($options['currentDir']) && ! isset($options['prependRelativePath'])) { + return $css; + } + require_once 'Minify/CSS/UriRewriter.php'; + if (isset($options['currentDir'])) { + return Minify_CSS_UriRewriter::rewrite( + $css + ,$options['currentDir'] + ,isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT'] + ,isset($options['symlinks']) ? $options['symlinks'] : array() + ); + } else { + return Minify_CSS_UriRewriter::prepend( + $css + ,$options['prependRelativePath'] + ); + } + } +} diff --git a/min/lib/Minify/CSS/Compressor.php b/min/lib/Minify/CSS/Compressor.php new file mode 100644 index 0000000..d483b2f --- /dev/null +++ b/min/lib/Minify/CSS/Compressor.php @@ -0,0 +1,250 @@ + + * @author http://code.google.com/u/1stvamp/ (Issue 64 patch) + */ +class Minify_CSS_Compressor { + + /** + * Minify a CSS string + * + * @param string $css + * + * @param array $options (currently ignored) + * + * @return string + */ + public static function process($css, $options = array()) + { + $obj = new Minify_CSS_Compressor($options); + return $obj->_process($css); + } + + /** + * @var array options + */ + protected $_options = null; + + /** + * @var bool Are we "in" a hack? + * + * I.e. are some browsers targetted until the next comment? + */ + protected $_inHack = false; + + + /** + * Constructor + * + * @param array $options (currently ignored) + * + * @return null + */ + private function __construct($options) { + $this->_options = $options; + } + + /** + * Minify a CSS string + * + * @param string $css + * + * @return string + */ + protected function _process($css) + { + $css = str_replace("\r\n", "\n", $css); + + // preserve empty comment after '>' + // http://www.webdevout.net/css-hacks#in_css-selectors + $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css); + + // preserve empty comment between property and value + // http://css-discuss.incutio.com/?page=BoxModelHack + $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css); + $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css); + + // apply callback to all valid comments (and strip out surrounding ws + $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@' + ,array($this, '_commentCB'), $css); + + // remove ws around { } and last semicolon in declaration block + $css = preg_replace('/\\s*{\\s*/', '{', $css); + $css = preg_replace('/;?\\s*}\\s*/', '}', $css); + + // remove ws surrounding semicolons + $css = preg_replace('/\\s*;\\s*/', ';', $css); + + // remove ws around urls + $css = preg_replace('/ + url\\( # url( + \\s* + ([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis) + \\s* + \\) # ) + /x', 'url($1)', $css); + + // remove ws between rules and colons + $css = preg_replace('/ + \\s* + ([{;]) # 1 = beginning of block or rule separator + \\s* + ([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter) + \\s* + : + \\s* + (\\b|[#\'"]) # 3 = first character of a value + /x', '$1$2:$3', $css); + + // remove ws in selectors + $css = preg_replace_callback('/ + (?: # non-capture + \\s* + [^~>+,\\s]+ # selector part + \\s* + [,>+~] # combinators + )+ + \\s* + [^~>+,\\s]+ # selector part + { # open declaration block + /x' + ,array($this, '_selectorsCB'), $css); + + // minimize hex colors + $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i' + , '$1#$2$3$4$5', $css); + + // remove spaces between font families + $css = preg_replace_callback('/font-family:([^;}]+)([;}])/' + ,array($this, '_fontFamilyCB'), $css); + + $css = preg_replace('/@import\\s+url/', '@import url', $css); + + // replace any ws involving newlines with a single newline + $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css); + + // separate common descendent selectors w/ newlines (to limit line lengths) + $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css); + + // Use newline after 1st numeric value (to limit line lengths). + $css = preg_replace('/ + ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value + \\s+ + /x' + ,"$1\n", $css); + + // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/ + $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css); + + return trim($css); + } + + /** + * Replace what looks like a set of selectors + * + * @param array $m regex matches + * + * @return string + */ + protected function _selectorsCB($m) + { + // remove ws around the combinators + return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]); + } + + /** + * Process a comment and return a replacement + * + * @param array $m regex matches + * + * @return string + */ + protected function _commentCB($m) + { + $hasSurroundingWs = (trim($m[0]) !== $m[1]); + $m = $m[1]; + // $m is the comment content w/o the surrounding tokens, + // but the return value will replace the entire comment. + if ($m === 'keep') { + return '/**/'; + } + if ($m === '" "') { + // component of http://tantek.com/CSS/Examples/midpass.html + return '/*" "*/'; + } + if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) { + // component of http://tantek.com/CSS/Examples/midpass.html + return '/*";}}/* */'; + } + if ($this->_inHack) { + // inversion: feeding only to one browser + if (preg_match('@ + ^/ # comment started like /*/ + \\s* + (\\S[\\s\\S]+?) # has at least some non-ws content + \\s* + /\\* # ends like /*/ or /**/ + @x', $m, $n)) { + // end hack mode after this comment, but preserve the hack and comment content + $this->_inHack = false; + return "/*/{$n[1]}/**/"; + } + } + if (substr($m, -1) === '\\') { // comment ends like \*/ + // begin hack mode and preserve hack + $this->_inHack = true; + return '/*\\*/'; + } + if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */ + // begin hack mode and preserve hack + $this->_inHack = true; + return '/*/*/'; + } + if ($this->_inHack) { + // a regular comment ends hack mode but should be preserved + $this->_inHack = false; + return '/**/'; + } + // Issue 107: if there's any surrounding whitespace, it may be important, so + // replace the comment with a single space + return $hasSurroundingWs // remove all other comments + ? ' ' + : ''; + } + + /** + * Process a font-family listing and return a replacement + * + * @param array $m regex matches + * + * @return string + */ + protected function _fontFamilyCB($m) + { + $m[1] = preg_replace('/ + \\s* + ( + "[^"]+" # 1 = family in double qutoes + |\'[^\']+\' # or 1 = family in single quotes + |[\\w\\-]+ # or 1 = unquoted family + ) + \\s* + /x', '$1', $m[1]); + return 'font-family:' . $m[1] . $m[2]; + } +} diff --git a/min/lib/Minify/CSS/UriRewriter.php b/min/lib/Minify/CSS/UriRewriter.php new file mode 100644 index 0000000..2b47cfc --- /dev/null +++ b/min/lib/Minify/CSS/UriRewriter.php @@ -0,0 +1,270 @@ + + */ +class Minify_CSS_UriRewriter { + + /** + * Defines which class to call as part of callbacks, change this + * if you extend Minify_CSS_UriRewriter + * @var string + */ + protected static $className = 'Minify_CSS_UriRewriter'; + + /** + * rewrite() and rewriteRelative() append debugging information here + * @var string + */ + public static $debugText = ''; + + /** + * Rewrite file relative URIs as root relative in CSS files + * + * @param string $css + * + * @param string $currentDir The directory of the current CSS file. + * + * @param string $docRoot The document root of the web site in which + * the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']). + * + * @param array $symlinks (default = array()) If the CSS file is stored in + * a symlink-ed directory, provide an array of link paths to + * target paths, where the link paths are within the document root. Because + * paths need to be normalized for this to work, use "//" to substitute + * the doc root in the link paths (the array keys). E.g.: + * + * array('//symlink' => '/real/target/path') // unix + * array('//static' => 'D:\\staticStorage') // Windows + * + * + * @return string + */ + public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array()) + { + self::$_docRoot = self::_realpath( + $docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT'] + ); + self::$_currentDir = self::_realpath($currentDir); + self::$_symlinks = array(); + + // normalize symlinks + foreach ($symlinks as $link => $target) { + $link = ($link === '//') + ? self::$_docRoot + : str_replace('//', self::$_docRoot . '/', $link); + $link = strtr($link, '/', DIRECTORY_SEPARATOR); + self::$_symlinks[$link] = self::_realpath($target); + } + + self::$debugText .= "docRoot : " . self::$_docRoot . "\n" + . "currentDir : " . self::$_currentDir . "\n"; + if (self::$_symlinks) { + self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n"; + } + self::$debugText .= "\n"; + + $css = self::_trimUrls($css); + + // rewrite + $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' + ,array(self::$className, '_processUriCB'), $css); + $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/' + ,array(self::$className, '_processUriCB'), $css); + + return $css; + } + + /** + * Prepend a path to relative URIs in CSS files + * + * @param string $css + * + * @param string $path The path to prepend. + * + * @return string + */ + public static function prepend($css, $path) + { + self::$_prependPath = $path; + + $css = self::_trimUrls($css); + + // append + $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/' + ,array(self::$className, '_processUriCB'), $css); + $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/' + ,array(self::$className, '_processUriCB'), $css); + + self::$_prependPath = null; + return $css; + } + + + /** + * @var string directory of this stylesheet + */ + private static $_currentDir = ''; + + /** + * @var string DOC_ROOT + */ + private static $_docRoot = ''; + + /** + * @var array directory replacements to map symlink targets back to their + * source (within the document root) E.g. '/var/www/symlink' => '/var/realpath' + */ + private static $_symlinks = array(); + + /** + * @var string path to prepend + */ + private static $_prependPath = null; + + private static function _trimUrls($css) + { + return preg_replace('/ + url\\( # url( + \\s* + ([^\\)]+?) # 1 = URI (assuming does not contain ")") + \\s* + \\) # ) + /x', 'url($1)', $css); + } + + private static function _processUriCB($m) + { + // $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/' + $isImport = ($m[0][0] === '@'); + // determine URI and the quote character (if any) + if ($isImport) { + $quoteChar = $m[1]; + $uri = $m[2]; + } else { + // $m[1] is either quoted or not + $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"') + ? $m[1][0] + : ''; + $uri = ($quoteChar === '') + ? $m[1] + : substr($m[1], 1, strlen($m[1]) - 2); + } + // analyze URI + if ('/' !== $uri[0] // root-relative + && false === strpos($uri, '//') // protocol (non-data) + && 0 !== strpos($uri, 'data:') // data protocol + ) { + // URI is file-relative: rewrite depending on options + $uri = (self::$_prependPath !== null) + ? (self::$_prependPath . $uri) + : self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks); + } + return $isImport + ? "@import {$quoteChar}{$uri}{$quoteChar}" + : "url({$quoteChar}{$uri}{$quoteChar})"; + } + + /** + * Rewrite a file relative URI as root relative + * + * + * Minify_CSS_UriRewriter::rewriteRelative( + * '../img/hello.gif' + * , '/home/user/www/css' // path of CSS file + * , '/home/user/www' // doc root + * ); + * // returns '/img/hello.gif' + * + * // example where static files are stored in a symlinked directory + * Minify_CSS_UriRewriter::rewriteRelative( + * 'hello.gif' + * , '/var/staticFiles/theme' + * , '/home/user/www' + * , array('/home/user/www/static' => '/var/staticFiles') + * ); + * // returns '/static/theme/hello.gif' + * + * + * @param string $uri file relative URI + * + * @param string $realCurrentDir realpath of the current file's directory. + * + * @param string $realDocRoot realpath of the site document root. + * + * @param array $symlinks (default = array()) If the file is stored in + * a symlink-ed directory, provide an array of link paths to + * real target paths, where the link paths "appear" to be within the document + * root. E.g.: + * + * array('/home/foo/www/not/real/path' => '/real/target/path') // unix + * array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows + * + * + * @return string + */ + public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array()) + { + // prepend path with current dir separator (OS-independent) + $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR) + . DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR); + + self::$debugText .= "file-relative URI : {$uri}\n" + . "path prepended : {$path}\n"; + + // "unresolve" a symlink back to doc root + foreach ($symlinks as $link => $target) { + if (0 === strpos($path, $target)) { + // replace $target with $link + $path = $link . substr($path, strlen($target)); + + self::$debugText .= "symlink unresolved : {$path}\n"; + + break; + } + } + // strip doc root + $path = substr($path, strlen($realDocRoot)); + + self::$debugText .= "docroot stripped : {$path}\n"; + + // fix to root-relative URI + + $uri = strtr($path, '/\\', '//'); + + // remove /./ and /../ where possible + $uri = str_replace('/./', '/', $uri); + // inspired by patch from Oleg Cherniy + do { + $uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed); + } while ($changed); + + self::$debugText .= "traversals removed : {$uri}\n\n"; + + return $uri; + } + + /** + * Get realpath with any trailing slash removed. If realpath() fails, + * just remove the trailing slash. + * + * @param string $path + * + * @return mixed path with no trailing slash + */ + protected static function _realpath($path) + { + $realPath = realpath($path); + if ($realPath !== false) { + $path = $realPath; + } + return rtrim($path, '/\\'); + } +} diff --git a/min/lib/Minify/Cache/APC.php b/min/lib/Minify/Cache/APC.php new file mode 100644 index 0000000..ca84d29 --- /dev/null +++ b/min/lib/Minify/Cache/APC.php @@ -0,0 +1,130 @@ + + * Minify::setCache(new Minify_Cache_APC()); + * + * + * @package Minify + * @author Chris Edwards + **/ +class Minify_Cache_APC { + + /** + * Create a Minify_Cache_APC object, to be passed to + * Minify::setCache(). + * + * + * @param int $expire seconds until expiration (default = 0 + * meaning the item will not get an expiration date) + * + * @return null + */ + public function __construct($expire = 0) + { + $this->_exp = $expire; + } + + /** + * Write data to cache. + * + * @param string $id cache id + * + * @param string $data + * + * @return bool success + */ + public function store($id, $data) + { + return apc_store($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp); + } + + /** + * Get the size of a cache entry + * + * @param string $id cache id + * + * @return int size in bytes + */ + public function getSize($id) + { + return $this->_fetch($id) + ? strlen($this->_data) + : false; + } + + /** + * Does a valid cache entry exist? + * + * @param string $id cache id + * + * @param int $srcMtime mtime of the original source file(s) + * + * @return bool exists + */ + public function isValid($id, $srcMtime) + { + return ($this->_fetch($id) && ($this->_lm >= $srcMtime)); + } + + /** + * Send the cached content to output + * + * @param string $id cache id + */ + public function display($id) + { + echo $this->_fetch($id) + ? $this->_data + : ''; + } + + /** + * Fetch the cached content + * + * @param string $id cache id + * + * @return string + */ + public function fetch($id) + { + return $this->_fetch($id) + ? $this->_data + : ''; + } + + private $_exp = null; + + // cache of most recently fetched id + private $_lm = null; + private $_data = null; + private $_id = null; + + /** + * Fetch data and timestamp from apc, store in instance + * + * @param string $id + * + * @return bool success + */ + private function _fetch($id) + { + if ($this->_id === $id) { + return true; + } + $ret = apc_fetch($id); + if (false === $ret) { + $this->_id = null; + return false; + } + list($this->_lm, $this->_data) = explode('|', $ret, 2); + $this->_id = $id; + return true; + } +} diff --git a/min/lib/Minify/Cache/File.php b/min/lib/Minify/Cache/File.php new file mode 100644 index 0000000..2753e6b --- /dev/null +++ b/min/lib/Minify/Cache/File.php @@ -0,0 +1,125 @@ +_locking = $fileLocking; + $this->_path = $path; + } + + /** + * Write data to cache. + * + * @param string $id cache id (e.g. a filename) + * + * @param string $data + * + * @return bool success + */ + public function store($id, $data) + { + $flag = $this->_locking + ? LOCK_EX + : null; + if (is_file($this->_path . '/' . $id)) { + @unlink($this->_path . '/' . $id); + } + if (! @file_put_contents($this->_path . '/' . $id, $data, $flag)) { + return false; + } + // write control + if ($data !== $this->fetch($id)) { + @unlink($file); + return false; + } + return true; + } + + /** + * Get the size of a cache entry + * + * @param string $id cache id (e.g. a filename) + * + * @return int size in bytes + */ + public function getSize($id) + { + return filesize($this->_path . '/' . $id); + } + + /** + * Does a valid cache entry exist? + * + * @param string $id cache id (e.g. a filename) + * + * @param int $srcMtime mtime of the original source file(s) + * + * @return bool exists + */ + public function isValid($id, $srcMtime) + { + $file = $this->_path . '/' . $id; + return (is_file($file) && (filemtime($file) >= $srcMtime)); + } + + /** + * Send the cached content to output + * + * @param string $id cache id (e.g. a filename) + */ + public function display($id) + { + if ($this->_locking) { + $fp = fopen($this->_path . '/' . $id, 'rb'); + flock($fp, LOCK_SH); + fpassthru($fp); + flock($fp, LOCK_UN); + fclose($fp); + } else { + readfile($this->_path . '/' . $id); + } + } + + /** + * Fetch the cached content + * + * @param string $id cache id (e.g. a filename) + * + * @return string + */ + public function fetch($id) + { + if ($this->_locking) { + $fp = fopen($this->_path . '/' . $id, 'rb'); + flock($fp, LOCK_SH); + $ret = stream_get_contents($fp); + flock($fp, LOCK_UN); + fclose($fp); + return $ret; + } else { + return file_get_contents($this->_path . '/' . $id); + } + } + + /** + * Fetch the cache path used + * + * @return string + */ + public function getPath() + { + return $this->_path; + } + + private $_path = null; + private $_locking = null; +} diff --git a/min/lib/Minify/Cache/Memcache.php b/min/lib/Minify/Cache/Memcache.php new file mode 100644 index 0000000..2b81e7a --- /dev/null +++ b/min/lib/Minify/Cache/Memcache.php @@ -0,0 +1,137 @@ + + * // fall back to disk caching if memcache can't connect + * $memcache = new Memcache; + * if ($memcache->connect('localhost', 11211)) { + * Minify::setCache(new Minify_Cache_Memcache($memcache)); + * } else { + * Minify::setCache(); + * } + * + **/ +class Minify_Cache_Memcache { + + /** + * Create a Minify_Cache_Memcache object, to be passed to + * Minify::setCache(). + * + * @param Memcache $memcache already-connected instance + * + * @param int $expire seconds until expiration (default = 0 + * meaning the item will not get an expiration date) + * + * @return null + */ + public function __construct($memcache, $expire = 0) + { + $this->_mc = $memcache; + $this->_exp = $expire; + } + + /** + * Write data to cache. + * + * @param string $id cache id + * + * @param string $data + * + * @return bool success + */ + public function store($id, $data) + { + return $this->_mc->set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", 0, $this->_exp); + } + + + /** + * Get the size of a cache entry + * + * @param string $id cache id + * + * @return int size in bytes + */ + public function getSize($id) + { + return $this->_fetch($id) + ? strlen($this->_data) + : false; + } + + /** + * Does a valid cache entry exist? + * + * @param string $id cache id + * + * @param int $srcMtime mtime of the original source file(s) + * + * @return bool exists + */ + public function isValid($id, $srcMtime) + { + return ($this->_fetch($id) && ($this->_lm >= $srcMtime)); + } + + /** + * Send the cached content to output + * + * @param string $id cache id + */ + public function display($id) + { + echo $this->_fetch($id) + ? $this->_data + : ''; + } + + /** + * Fetch the cached content + * + * @param string $id cache id + * + * @return string + */ + public function fetch($id) + { + return $this->_fetch($id) + ? $this->_data + : ''; + } + + private $_mc = null; + private $_exp = null; + + // cache of most recently fetched id + private $_lm = null; + private $_data = null; + private $_id = null; + + /** + * Fetch data and timestamp from memcache, store in instance + * + * @param string $id + * + * @return bool success + */ + private function _fetch($id) + { + if ($this->_id === $id) { + return true; + } + $ret = $this->_mc->get($id); + if (false === $ret) { + $this->_id = null; + return false; + } + list($this->_lm, $this->_data) = explode('|', $ret, 2); + $this->_id = $id; + return true; + } +} diff --git a/min/lib/Minify/CommentPreserver.php b/min/lib/Minify/CommentPreserver.php new file mode 100644 index 0000000..f56eb34 --- /dev/null +++ b/min/lib/Minify/CommentPreserver.php @@ -0,0 +1,90 @@ + + */ +class Minify_CommentPreserver { + + /** + * String to be prepended to each preserved comment + * + * @var string + */ + public static $prepend = "\n"; + + /** + * String to be appended to each preserved comment + * + * @var string + */ + public static $append = "\n"; + + /** + * Process a string outside of C-style comments that begin with "/*!" + * + * On each non-empty string outside these comments, the given processor + * function will be called. The first "!" will be removed from the + * preserved comments, and the comments will be surrounded by + * Minify_CommentPreserver::$preprend and Minify_CommentPreserver::$append. + * + * @param string $content + * @param callback $processor function + * @param array $args array of extra arguments to pass to the processor + * function (default = array()) + * @return string + */ + public static function process($content, $processor, $args = array()) + { + $ret = ''; + while (true) { + list($beforeComment, $comment, $afterComment) = self::_nextComment($content); + if ('' !== $beforeComment) { + $callArgs = $args; + array_unshift($callArgs, $beforeComment); + $ret .= call_user_func_array($processor, $callArgs); + } + if (false === $comment) { + break; + } + $ret .= $comment; + $content = $afterComment; + } + return $ret; + } + + /** + * Extract comments that YUI Compressor preserves. + * + * @param string $in input + * + * @return array 3 elements are returned. If a YUI comment is found, the + * 2nd element is the comment and the 1st and 2nd are the surrounding + * strings. If no comment is found, the entire string is returned as the + * 1st element and the other two are false. + */ + private static function _nextComment($in) + { + if ( + false === ($start = strpos($in, '/*!')) + || false === ($end = strpos($in, '*/', $start + 3)) + ) { + return array($in, false, false); + } + $ret = array( + substr($in, 0, $start) + ,self::$prepend . '/*' . substr($in, $start + 3, $end - $start - 1) . self::$append + ); + $endChars = (strlen($in) - $end - 2); + $ret[] = (0 === $endChars) + ? '' + : substr($in, -$endChars); + return $ret; + } +} diff --git a/min/lib/Minify/Controller/Base.php b/min/lib/Minify/Controller/Base.php new file mode 100644 index 0000000..84889b3 --- /dev/null +++ b/min/lib/Minify/Controller/Base.php @@ -0,0 +1,202 @@ + + */ +abstract class Minify_Controller_Base { + + /** + * Setup controller sources and set an needed options for Minify::source + * + * You must override this method in your subclass controller to set + * $this->sources. If the request is NOT valid, make sure $this->sources + * is left an empty array. Then strip any controller-specific options from + * $options and return it. To serve files, $this->sources must be an array of + * Minify_Source objects. + * + * @param array $options controller and Minify options + * + * return array $options Minify::serve options + */ + abstract public function setupSources($options); + + /** + * Get default Minify options for this controller. + * + * Override in subclass to change defaults + * + * @return array options for Minify + */ + public function getDefaultMinifyOptions() { + return array( + 'isPublic' => true + ,'encodeOutput' => function_exists('gzdeflate') + ,'encodeMethod' => null // determine later + ,'encodeLevel' => 9 + ,'minifierOptions' => array() // no minifier options + ,'contentTypeCharset' => 'utf-8' + ,'maxAge' => 1800 // 30 minutes + ,'rewriteCssUris' => true + ,'bubbleCssImports' => false + ,'quiet' => false // serve() will send headers and output + ,'debug' => false + + // if you override this, the response code MUST be directly after + // the first space. + ,'badRequestHeader' => 'HTTP/1.0 400 Bad Request' + + // callback function to see/modify content of all sources + ,'postprocessor' => null + // file to require to load preprocessor + ,'postprocessorRequire' => null + ); + } + + /** + * Get default minifiers for this controller. + * + * Override in subclass to change defaults + * + * @return array minifier callbacks for common types + */ + public function getDefaultMinifers() { + $ret[Minify::TYPE_JS] = array('JSMin', 'minify'); + $ret[Minify::TYPE_CSS] = array('Minify_CSS', 'minify'); + $ret[Minify::TYPE_HTML] = array('Minify_HTML', 'minify'); + return $ret; + } + + /** + * Load any code necessary to execute the given minifier callback. + * + * The controller is responsible for loading minification code on demand + * via this method. This built-in function will only load classes for + * static method callbacks where the class isn't already defined. It uses + * the PEAR convention, so, given array('Jimmy_Minifier', 'minCss'), this + * function will include 'Jimmy/Minifier.php'. + * + * If you need code loaded on demand and this doesn't suit you, you'll need + * to override this function in your subclass. + * @see Minify_Controller_Page::loadMinifier() + * + * @param callback $minifierCallback callback of minifier function + * + * @return null + */ + public function loadMinifier($minifierCallback) + { + if (is_array($minifierCallback) + && is_string($minifierCallback[0]) + && !class_exists($minifierCallback[0], false)) { + + require str_replace('_', '/', $minifierCallback[0]) . '.php'; + } + } + + /** + * Is a user-given file within an allowable directory, existing, + * and having an extension js/css/html/txt ? + * + * This is a convenience function for controllers that have to accept + * user-given paths + * + * @param string $file full file path (already processed by realpath()) + * + * @param array $safeDirs directories where files are safe to serve. Files can also + * be in subdirectories of these directories. + * + * @return bool file is safe + */ + public static function _fileIsSafe($file, $safeDirs) + { + $pathOk = false; + foreach ((array)$safeDirs as $safeDir) { + if (strpos($file, $safeDir) === 0) { + $pathOk = true; + break; + } + } + $base = basename($file); + if (! $pathOk || ! is_file($file) || $base[0] === '.') { + return false; + } + list($revExt) = explode('.', strrev($base)); + return in_array(strrev($revExt), array('js', 'css', 'html', 'txt')); + } + + /** + * @var array instances of Minify_Source, which provide content and + * any individual minification needs. + * + * @see Minify_Source + */ + public $sources = array(); + + /** + * Mix in default controller options with user-given options + * + * @param array $options user options + * + * @return array mixed options + */ + public final function mixInDefaultOptions($options) + { + $ret = array_merge( + $this->getDefaultMinifyOptions(), $options + ); + if (! isset($options['minifiers'])) { + $options['minifiers'] = array(); + } + $ret['minifiers'] = array_merge( + $this->getDefaultMinifers(), $options['minifiers'] + ); + return $ret; + } + + /** + * Analyze sources (if there are any) and set $options 'contentType' + * and 'lastModifiedTime' if they already aren't. + * + * @param array $options options for Minify + * + * @return array options for Minify + */ + public final function analyzeSources($options = array()) + { + if ($this->sources) { + if (! isset($options['contentType'])) { + $options['contentType'] = Minify_Source::getContentType($this->sources); + } + // last modified is needed for caching, even if setExpires is set + if (! isset($options['lastModifiedTime'])) { + $max = 0; + foreach ($this->sources as $source) { + $max = max($source->lastModified, $max); + } + $options['lastModifiedTime'] = $max; + } + } + return $options; + } + + /** + * Send message to the Minify logger + * @param string $msg + * @return null + */ + protected function log($msg) { + require_once 'Minify/Logger.php'; + Minify_Logger::log($msg); + } +} diff --git a/min/lib/Minify/Controller/Files.php b/min/lib/Minify/Controller/Files.php new file mode 100644 index 0000000..83f028a --- /dev/null +++ b/min/lib/Minify/Controller/Files.php @@ -0,0 +1,78 @@ + + * Minify::serve('Files', array( + * 'files' => array( + * '//js/jquery.js' + * ,'//js/plugins.js' + * ,'/home/username/file.js' + * ) + * )); + * + * + * As a shortcut, the controller will replace "//" at the beginning + * of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'. + * + * @package Minify + * @author Stephen Clay + */ +class Minify_Controller_Files extends Minify_Controller_Base { + + /** + * Set up file sources + * + * @param array $options controller and Minify options + * @return array Minify options + * + * Controller options: + * + * 'files': (required) array of complete file paths, or a single path + */ + public function setupSources($options) { + // strip controller options + + $files = $options['files']; + // if $files is a single object, casting will break it + if (is_object($files)) { + $files = array($files); + } elseif (! is_array($files)) { + $files = (array)$files; + } + unset($options['files']); + + $sources = array(); + foreach ($files as $file) { + if ($file instanceof Minify_Source) { + $sources[] = $file; + continue; + } + if (0 === strpos($file, '//')) { + $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); + } + $realPath = realpath($file); + if (is_file($realPath)) { + $sources[] = new Minify_Source(array( + 'filepath' => $realPath + )); + } else { + $this->log("The path \"{$file}\" could not be found (or was not a file)"); + return $options; + } + } + if ($sources) { + $this->sources = $sources; + } + return $options; + } +} + diff --git a/min/lib/Minify/Controller/Groups.php b/min/lib/Minify/Controller/Groups.php new file mode 100644 index 0000000..1ac5770 --- /dev/null +++ b/min/lib/Minify/Controller/Groups.php @@ -0,0 +1,94 @@ + + * Minify::serve('Groups', array( + * 'groups' => array( + * 'css' => array('//css/type.css', '//css/layout.css') + * ,'js' => array('//js/jquery.js', '//js/site.js') + * ) + * )); + * + * + * If the above code were placed in /serve.php, it would enable the URLs + * /serve.php/js and /serve.php/css + * + * As a shortcut, the controller will replace "//" at the beginning + * of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'. + * + * @package Minify + * @author Stephen Clay + */ +class Minify_Controller_Groups extends Minify_Controller_Base { + + /** + * Set up groups of files as sources + * + * @param array $options controller and Minify options + * @return array Minify options + * + * Controller options: + * + * 'groups': (required) array mapping PATH_INFO strings to arrays + * of complete file paths. @see Minify_Controller_Groups + */ + public function setupSources($options) { + // strip controller options + $groups = $options['groups']; + unset($options['groups']); + + // mod_fcgid places PATH_INFO in ORIG_PATH_INFO + $pi = isset($_SERVER['ORIG_PATH_INFO']) + ? substr($_SERVER['ORIG_PATH_INFO'], 1) + : (isset($_SERVER['PATH_INFO']) + ? substr($_SERVER['PATH_INFO'], 1) + : false + ); + if (false === $pi || ! isset($groups[$pi])) { + // no PATH_INFO or not a valid group + $this->log("Missing PATH_INFO or no group set for \"$pi\""); + return $options; + } + $sources = array(); + + $files = $groups[$pi]; + // if $files is a single object, casting will break it + if (is_object($files)) { + $files = array($files); + } elseif (! is_array($files)) { + $files = (array)$files; + } + foreach ($files as $file) { + if ($file instanceof Minify_Source) { + $sources[] = $file; + continue; + } + if (0 === strpos($file, '//')) { + $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); + } + $realPath = realpath($file); + if (is_file($realPath)) { + $sources[] = new Minify_Source(array( + 'filepath' => $realPath + )); + } else { + $this->log("The path \"{$file}\" could not be found (or was not a file)"); + return $options; + } + } + if ($sources) { + $this->sources = $sources; + } + return $options; + } +} + diff --git a/min/lib/Minify/Controller/MinApp.php b/min/lib/Minify/Controller/MinApp.php new file mode 100644 index 0000000..9582d29 --- /dev/null +++ b/min/lib/Minify/Controller/MinApp.php @@ -0,0 +1,132 @@ + + */ +class Minify_Controller_MinApp extends Minify_Controller_Base { + + /** + * Set up groups of files as sources + * + * @param array $options controller and Minify options + * @return array Minify options + * + */ + public function setupSources($options) { + // filter controller options + $cOptions = array_merge( + array( + 'allowDirs' => '//' + ,'groupsOnly' => false + ,'groups' => array() + ,'maxFiles' => 10 + ) + ,(isset($options['minApp']) ? $options['minApp'] : array()) + ); + unset($options['minApp']); + $sources = array(); + if (isset($_GET['g'])) { + // try groups + if (! isset($cOptions['groups'][$_GET['g']])) { + $this->log("A group configuration for \"{$_GET['g']}\" was not set"); + return $options; + } + + $files = $cOptions['groups'][$_GET['g']]; + // if $files is a single object, casting will break it + if (is_object($files)) { + $files = array($files); + } elseif (! is_array($files)) { + $files = (array)$files; + } + foreach ($files as $file) { + if ($file instanceof Minify_Source) { + $sources[] = $file; + continue; + } + if (0 === strpos($file, '//')) { + $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1); + } + $file = realpath($file); + if (is_file($file)) { + $sources[] = new Minify_Source(array( + 'filepath' => $file + )); + } else { + $this->log("The path \"{$file}\" could not be found (or was not a file)"); + return $options; + } + } + } elseif (! $cOptions['groupsOnly'] && isset($_GET['f'])) { + // try user files + // The following restrictions are to limit the URLs that minify will + // respond to. Ideally there should be only one way to reference a file. + if (// verify at least one file, files are single comma separated, + // and are all same extension + ! preg_match('/^[^,]+\\.(css|js)(?:,[^,]+\\.\\1)*$/', $_GET['f']) + // no "//" + || strpos($_GET['f'], '//') !== false + // no "\" + || strpos($_GET['f'], '\\') !== false + // no "./" + || preg_match('/(?:^|[^\\.])\\.\\//', $_GET['f']) + ) { + $this->log("GET param 'f' invalid (see MinApp.php line 63)"); + return $options; + } + $files = explode(',', $_GET['f']); + if (count($files) > $cOptions['maxFiles'] || $files != array_unique($files)) { + $this->log("Too many or duplicate files specified"); + return $options; + } + if (isset($_GET['b'])) { + // check for validity + if (preg_match('@^[^/]+(?:/[^/]+)*$@', $_GET['b']) + && false === strpos($_GET['b'], '..') + && $_GET['b'] !== '.') { + // valid base + $base = "/{$_GET['b']}/"; + } else { + $this->log("GET param 'b' invalid (see MinApp.php line 84)"); + return $options; + } + } else { + $base = '/'; + } + $allowDirs = array(); + foreach ((array)$cOptions['allowDirs'] as $allowDir) { + $allowDirs[] = realpath(str_replace('//', $_SERVER['DOCUMENT_ROOT'] . '/', $allowDir)); + } + foreach ($files as $file) { + $path = $_SERVER['DOCUMENT_ROOT'] . $base . $file; + $file = realpath($path); + if (false === $file) { + $this->log("Path \"{$path}\" failed realpath()"); + return $options; + } elseif (! parent::_fileIsSafe($file, $allowDirs)) { + $this->log("Path \"{$path}\" failed Minify_Controller_Base::_fileIsSafe()"); + return $options; + } else { + $sources[] = new Minify_Source(array( + 'filepath' => $file + )); + } + } + } + if ($sources) { + $this->sources = $sources; + } else { + $this->log("No sources to serve"); + } + return $options; + } +} diff --git a/min/lib/Minify/Controller/Page.php b/min/lib/Minify/Controller/Page.php new file mode 100644 index 0000000..fa4599a --- /dev/null +++ b/min/lib/Minify/Controller/Page.php @@ -0,0 +1,82 @@ + + */ +class Minify_Controller_Page extends Minify_Controller_Base { + + /** + * Set up source of HTML content + * + * @param array $options controller and Minify options + * @return array Minify options + * + * Controller options: + * + * 'content': (required) HTML markup + * + * 'id': (required) id of page (string for use in server-side caching) + * + * 'lastModifiedTime': timestamp of when this content changed. This + * is recommended to allow both server and client-side caching. + * + * 'minifyAll': should all CSS and Javascript blocks be individually + * minified? (default false) + * + * @todo Add 'file' option to read HTML file. + */ + public function setupSources($options) { + if (isset($options['file'])) { + $sourceSpec = array( + 'filepath' => $options['file'] + ); + } else { + // strip controller options + $sourceSpec = array( + 'content' => $options['content'] + ,'id' => $options['id'] + ); + unset($options['content'], $options['id']); + } + if (isset($options['minifyAll'])) { + // this will be the 2nd argument passed to Minify_HTML::minify() + $sourceSpec['minifyOptions'] = array( + 'cssMinifier' => array('Minify_CSS', 'minify') + ,'jsMinifier' => array('JSMin', 'minify') + ); + $this->_loadCssJsMinifiers = true; + unset($options['minifyAll']); + } + $this->sources[] = new Minify_Source($sourceSpec); + + $options['contentType'] = Minify::TYPE_HTML; + return $options; + } + + protected $_loadCssJsMinifiers = false; + + /** + * @see Minify_Controller_Base::loadMinifier() + */ + public function loadMinifier($minifierCallback) + { + if ($this->_loadCssJsMinifiers) { + // Minify will not call for these so we must manually load + // them when Minify/HTML.php is called for. + require_once 'Minify/CSS.php'; + require_once 'JSMin.php'; + } + parent::loadMinifier($minifierCallback); // load Minify/HTML.php + } +} + diff --git a/min/lib/Minify/Controller/Version1.php b/min/lib/Minify/Controller/Version1.php new file mode 100644 index 0000000..1861aab --- /dev/null +++ b/min/lib/Minify/Controller/Version1.php @@ -0,0 +1,118 @@ + + * Minify::serve('Version1'); + * + * + * @package Minify + * @author Stephen Clay + */ +class Minify_Controller_Version1 extends Minify_Controller_Base { + + /** + * Set up groups of files as sources + * + * @param array $options controller and Minify options + * @return array Minify options + * + */ + public function setupSources($options) { + self::_setupDefines(); + if (MINIFY_USE_CACHE) { + $cacheDir = defined('MINIFY_CACHE_DIR') + ? MINIFY_CACHE_DIR + : ''; + Minify::setCache($cacheDir); + } + $options['badRequestHeader'] = 'HTTP/1.0 404 Not Found'; + $options['contentTypeCharset'] = MINIFY_ENCODING; + + // The following restrictions are to limit the URLs that minify will + // respond to. Ideally there should be only one way to reference a file. + if (! isset($_GET['files']) + // verify at least one file, files are single comma separated, + // and are all same extension + || ! preg_match('/^[^,]+\\.(css|js)(,[^,]+\\.\\1)*$/', $_GET['files'], $m) + // no "//" (makes URL rewriting easier) + || strpos($_GET['files'], '//') !== false + // no "\" + || strpos($_GET['files'], '\\') !== false + // no "./" + || preg_match('/(?:^|[^\\.])\\.\\//', $_GET['files']) + ) { + return $options; + } + $extension = $m[1]; + + $files = explode(',', $_GET['files']); + if (count($files) > MINIFY_MAX_FILES) { + return $options; + } + + // strings for prepending to relative/absolute paths + $prependRelPaths = dirname($_SERVER['SCRIPT_FILENAME']) + . DIRECTORY_SEPARATOR; + $prependAbsPaths = $_SERVER['DOCUMENT_ROOT']; + + $sources = array(); + $goodFiles = array(); + $hasBadSource = false; + + $allowDirs = isset($options['allowDirs']) + ? $options['allowDirs'] + : MINIFY_BASE_DIR; + + foreach ($files as $file) { + // prepend appropriate string for abs/rel paths + $file = ($file[0] === '/' ? $prependAbsPaths : $prependRelPaths) . $file; + // make sure a real file! + $file = realpath($file); + // don't allow unsafe or duplicate files + if (parent::_fileIsSafe($file, $allowDirs) + && !in_array($file, $goodFiles)) + { + $goodFiles[] = $file; + $srcOptions = array( + 'filepath' => $file + ); + $this->sources[] = new Minify_Source($srcOptions); + } else { + $hasBadSource = true; + break; + } + } + if ($hasBadSource) { + $this->sources = array(); + } + if (! MINIFY_REWRITE_CSS_URLS) { + $options['rewriteCssUris'] = false; + } + return $options; + } + + private static function _setupDefines() + { + $defaults = array( + 'MINIFY_BASE_DIR' => realpath($_SERVER['DOCUMENT_ROOT']) + ,'MINIFY_ENCODING' => 'utf-8' + ,'MINIFY_MAX_FILES' => 16 + ,'MINIFY_REWRITE_CSS_URLS' => true + ,'MINIFY_USE_CACHE' => true + ); + foreach ($defaults as $const => $val) { + if (! defined($const)) { + define($const, $val); + } + } + } +} + diff --git a/min/lib/Minify/HTML.php b/min/lib/Minify/HTML.php new file mode 100644 index 0000000..fb5c1e9 --- /dev/null +++ b/min/lib/Minify/HTML.php @@ -0,0 +1,245 @@ + + */ +class Minify_HTML { + + /** + * "Minify" an HTML page + * + * @param string $html + * + * @param array $options + * + * 'cssMinifier' : (optional) callback function to process content of STYLE + * elements. + * + * 'jsMinifier' : (optional) callback function to process content of SCRIPT + * elements. Note: the type attribute is ignored. + * + * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If + * unset, minify will sniff for an XHTML doctype. + * + * @return string + */ + public static function minify($html, $options = array()) { + $min = new Minify_HTML($html, $options); + return $min->process(); + } + + + /** + * Create a minifier object + * + * @param string $html + * + * @param array $options + * + * 'cssMinifier' : (optional) callback function to process content of STYLE + * elements. + * + * 'jsMinifier' : (optional) callback function to process content of SCRIPT + * elements. Note: the type attribute is ignored. + * + * 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If + * unset, minify will sniff for an XHTML doctype. + * + * @return null + */ + public function __construct($html, $options = array()) + { + $this->_html = str_replace("\r\n", "\n", trim($html)); + if (isset($options['xhtml'])) { + $this->_isXhtml = (bool)$options['xhtml']; + } + if (isset($options['cssMinifier'])) { + $this->_cssMinifier = $options['cssMinifier']; + } + if (isset($options['jsMinifier'])) { + $this->_jsMinifier = $options['jsMinifier']; + } + } + + + /** + * Minify the markeup given in the constructor + * + * @return string + */ + public function process() + { + if ($this->_isXhtml === null) { + $this->_isXhtml = (false !== strpos($this->_html, '_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']); + $this->_placeholders = array(); + + // replace SCRIPTs (and minify) with placeholders + $this->_html = preg_replace_callback( + '/(\\s*)(]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i' + ,array($this, '_removeScriptCB') + ,$this->_html); + + // replace STYLEs (and minify) with placeholders + $this->_html = preg_replace_callback( + '/\\s*(]*?>)([\\s\\S]*?)<\\/style>\\s*/i' + ,array($this, '_removeStyleCB') + ,$this->_html); + + // remove HTML comments (not containing IE conditional comments). + $this->_html = preg_replace_callback( + '//' + ,array($this, '_commentCB') + ,$this->_html); + + // replace PREs with placeholders + $this->_html = preg_replace_callback('/\\s*(]*?>[\\s\\S]*?<\\/pre>)\\s*/i' + ,array($this, '_removePreCB') + ,$this->_html); + + // replace TEXTAREAs with placeholders + $this->_html = preg_replace_callback( + '/\\s*(]*?>[\\s\\S]*?<\\/textarea>)\\s*/i' + ,array($this, '_removeTextareaCB') + ,$this->_html); + + // trim each line. + // @todo take into account attribute values that span multiple lines. + $this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html); + + // remove ws around block/undisplayed elements + $this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body' + .'|caption|center|cite|col(?:group)?|dd|dir|div|dl|dt|fieldset|form' + .'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta' + .'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)' + .'|ul)\\b[^>]*>)/i', '$1', $this->_html); + + // remove ws outside of all elements + $this->_html = preg_replace_callback( + '/>([^<]+)_html); + + // use newlines before 1st attribute in open tags (to limit line lengths) + $this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html); + + // fill placeholders + $this->_html = str_replace( + array_keys($this->_placeholders) + ,array_values($this->_placeholders) + ,$this->_html + ); + return $this->_html; + } + + protected function _commentCB($m) + { + return (0 === strpos($m[1], '[') || false !== strpos($m[1], '_replacementHash . count($this->_placeholders) . '%'; + $this->_placeholders[$placeholder] = $content; + return $placeholder; + } + + protected $_isXhtml = null; + protected $_replacementHash = null; + protected $_placeholders = array(); + protected $_cssMinifier = null; + protected $_jsMinifier = null; + + protected function _outsideTagCB($m) + { + return '>' . preg_replace('/^\\s+|\\s+$/', ' ', $m[1]) . '<'; + } + + protected function _removePreCB($m) + { + return $this->_reservePlace($m[1]); + } + + protected function _removeTextareaCB($m) + { + return $this->_reservePlace($m[1]); + } + + protected function _removeStyleCB($m) + { + $openStyle = $m[1]; + $css = $m[2]; + // remove HTML comments + $css = preg_replace('/(?:^\\s*\\s*$)/', '', $css); + + // remove CDATA section markers + $css = $this->_removeCdata($css); + + // minify + $minifier = $this->_cssMinifier + ? $this->_cssMinifier + : 'trim'; + $css = call_user_func($minifier, $css); + + return $this->_reservePlace($this->_needsCdata($css) + ? "{$openStyle}/**/" + : "{$openStyle}{$css}" + ); + } + + protected function _removeScriptCB($m) + { + $openScript = $m[2]; + $js = $m[3]; + + // whitespace surrounding? preserve at least one space + $ws1 = ($m[1] === '') ? '' : ' '; + $ws2 = ($m[4] === '') ? '' : ' '; + + // remove HTML comments (and ending "//" if present) + $js = preg_replace('/(?:^\\s*\\s*$)/', '', $js); + + // remove CDATA section markers + $js = $this->_removeCdata($js); + + // minify + $minifier = $this->_jsMinifier + ? $this->_jsMinifier + : 'trim'; + $js = call_user_func($minifier, $js); + + return $this->_reservePlace($this->_needsCdata($js) + ? "{$ws1}{$openScript}/**/{$ws2}" + : "{$ws1}{$openScript}{$js}{$ws2}" + ); + } + + protected function _removeCdata($str) + { + return (false !== strpos($str, ''), '', $str) + : $str; + } + + protected function _needsCdata($str) + { + return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str)); + } +} diff --git a/min/lib/Minify/ImportProcessor.php b/min/lib/Minify/ImportProcessor.php new file mode 100644 index 0000000..0d6d90a --- /dev/null +++ b/min/lib/Minify/ImportProcessor.php @@ -0,0 +1,157 @@ + + */ +class Minify_ImportProcessor { + + public static $filesIncluded = array(); + + public static function process($file) + { + self::$filesIncluded = array(); + self::$_isCss = (strtolower(substr($file, -4)) === '.css'); + $obj = new Minify_ImportProcessor(dirname($file)); + return $obj->_getContent($file); + } + + // allows callback funcs to know the current directory + private $_currentDir = null; + + // allows _importCB to write the fetched content back to the obj + private $_importedContent = ''; + + private static $_isCss = null; + + private function __construct($currentDir) + { + $this->_currentDir = $currentDir; + } + + private function _getContent($file) + { + $file = realpath($file); + if (! $file + || in_array($file, self::$filesIncluded) + || false === ($content = @file_get_contents($file)) + ) { + // file missing, already included, or failed read + return ''; + } + self::$filesIncluded[] = realpath($file); + $this->_currentDir = dirname($file); + + // remove UTF-8 BOM if present + if (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) { + $content = substr($content, 3); + } + // ensure uniform EOLs + $content = str_replace("\r\n", "\n", $content); + + // process @imports + $content = preg_replace_callback( + '/ + @import\\s+ + (?:url\\(\\s*)? # maybe url( + [\'"]? # maybe quote + (.*?) # 1 = URI + [\'"]? # maybe end quote + (?:\\s*\\))? # maybe ) + ([a-zA-Z,\\s]*)? # 2 = media list + ; # end token + /x' + ,array($this, '_importCB') + ,$content + ); + + if (self::$_isCss) { + // rewrite remaining relative URIs + $content = preg_replace_callback( + '/url\\(\\s*([^\\)\\s]+)\\s*\\)/' + ,array($this, '_urlCB') + ,$content + ); + } + + return $this->_importedContent . $content; + } + + private function _importCB($m) + { + $url = $m[1]; + $mediaList = preg_replace('/\\s+/', '', $m[2]); + + if (strpos($url, '://') > 0) { + // protocol, leave in place for CSS, comment for JS + return self::$_isCss + ? $m[0] + : "/* Minify_ImportProcessor will not include remote content */"; + } + if ('/' === $url[0]) { + // protocol-relative or root path + $url = ltrim($url, '/'); + $file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR + . strtr($url, '/', DIRECTORY_SEPARATOR); + } else { + // relative to current path + $file = $this->_currentDir . DIRECTORY_SEPARATOR + . strtr($url, '/', DIRECTORY_SEPARATOR); + } + $obj = new Minify_ImportProcessor(dirname($file)); + $content = $obj->_getContent($file); + if ('' === $content) { + // failed. leave in place for CSS, comment for JS + return self::$_isCss + ? $m[0] + : "/* Minify_ImportProcessor could not fetch '{$file}' */";; + } + return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList)) + ? $content + : "@media {$mediaList} {\n{$content}\n}\n"; + } + + private function _urlCB($m) + { + // $m[1] is either quoted or not + $quote = ($m[1][0] === "'" || $m[1][0] === '"') + ? $m[1][0] + : ''; + $url = ($quote === '') + ? $m[1] + : substr($m[1], 1, strlen($m[1]) - 2); + if ('/' !== $url[0]) { + if (strpos($url, '//') > 0) { + // probably starts with protocol, do not alter + } else { + // prepend path with current dir separator (OS-independent) + $path = $this->_currentDir + . DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR); + // strip doc root + $path = substr($path, strlen(realpath($_SERVER['DOCUMENT_ROOT']))); + // fix to absolute URL + $url = strtr($path, '/\\', '//'); + // remove /./ and /../ where possible + $url = str_replace('/./', '/', $url); + // inspired by patch from Oleg Cherniy + do { + $url = preg_replace('@/[^/]+/\\.\\./@', '/', $url, 1, $changed); + } while ($changed); + } + } + return "url({$quote}{$url}{$quote})"; + } +} diff --git a/min/lib/Minify/Lines.php b/min/lib/Minify/Lines.php new file mode 100644 index 0000000..186e701 --- /dev/null +++ b/min/lib/Minify/Lines.php @@ -0,0 +1,131 @@ + + * @author Adam Pedersen (Issue 55 fix) + */ +class Minify_Lines { + + /** + * Add line numbers in C-style comments + * + * This uses a very basic parser easily fooled by comment tokens inside + * strings or regexes, but, otherwise, generally clean code will not be + * mangled. URI rewriting can also be performed. + * + * @param string $content + * + * @param array $options available options: + * + * 'id': (optional) string to identify file. E.g. file name/path + * + * 'currentDir': (default null) if given, this is assumed to be the + * directory of the current CSS file. Using this, minify will rewrite + * all relative URIs in import/url declarations to correctly point to + * the desired files, and prepend a comment with debugging information about + * this process. + * + * @return string + */ + public static function minify($content, $options = array()) + { + $id = (isset($options['id']) && $options['id']) + ? $options['id'] + : ''; + $content = str_replace("\r\n", "\n", $content); + $lines = explode("\n", $content); + $numLines = count($lines); + // determine left padding + $padTo = strlen($numLines); + $inComment = false; + $i = 0; + $newLines = array(); + while (null !== ($line = array_shift($lines))) { + if (('' !== $id) && (0 == $i % 50)) { + array_push($newLines, '', "/* {$id} */", ''); + } + ++$i; + $newLines[] = self::_addNote($line, $i, $inComment, $padTo); + $inComment = self::_eolInComment($line, $inComment); + } + $content = implode("\n", $newLines) . "\n"; + + // check for desired URI rewriting + if (isset($options['currentDir'])) { + require_once 'Minify/CSS/UriRewriter.php'; + Minify_CSS_UriRewriter::$debugText = ''; + $content = Minify_CSS_UriRewriter::rewrite( + $content + ,$options['currentDir'] + ,isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT'] + ,isset($options['symlinks']) ? $options['symlinks'] : array() + ); + $content = "/* Minify_CSS_UriRewriter::\$debugText\n\n" + . Minify_CSS_UriRewriter::$debugText . "*/\n" + . $content; + } + + return $content; + } + + /** + * Is the parser within a C-style comment at the end of this line? + * + * @param string $line current line of code + * + * @param bool $inComment was the parser in a comment at the + * beginning of the line? + * + * @return bool + */ + private static function _eolInComment($line, $inComment) + { + while (strlen($line)) { + $search = $inComment + ? '*/' + : '/*'; + $pos = strpos($line, $search); + if (false === $pos) { + return $inComment; + } else { + if ($pos == 0 + || ($inComment + ? substr($line, $pos, 3) + : substr($line, $pos-1, 3)) != '*/*') + { + $inComment = ! $inComment; + } + $line = substr($line, $pos + 2); + } + } + return $inComment; + } + + /** + * Prepend a comment (or note) to the given line + * + * @param string $line current line of code + * + * @param string $note content of note/comment + * + * @param bool $inComment was the parser in a comment at the + * beginning of the line? + * + * @param int $padTo minimum width of comment + * + * @return string + */ + private static function _addNote($line, $note, $inComment, $padTo) + { + return $inComment + ? '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' *| ' . $line + : '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' */ ' . $line; + } +} diff --git a/min/lib/Minify/Logger.php b/min/lib/Minify/Logger.php new file mode 100644 index 0000000..7844eea --- /dev/null +++ b/min/lib/Minify/Logger.php @@ -0,0 +1,45 @@ + + */ +class Minify_Logger { + + /** + * Set logger object. + * + * The object should have a method "log" that accepts a value as 1st argument and + * an optional string label as the 2nd. + * + * @param mixed $obj or a "falsey" value to disable + * @return null + */ + public static function setLogger($obj = null) { + self::$_logger = $obj + ? $obj + : null; + } + + /** + * Pass a message to the logger (if set) + * + * @param string $msg message to log + * @return null + */ + public static function log($msg, $label = 'Minify') { + if (! self::$_logger) return; + self::$_logger->log($msg, $label); + } + + /** + * @var mixed logger object (like FirePHP) or null (i.e. no logger available) + */ + private static $_logger = null; +} diff --git a/min/lib/Minify/Packer.php b/min/lib/Minify/Packer.php new file mode 100644 index 0000000..949c3ee --- /dev/null +++ b/min/lib/Minify/Packer.php @@ -0,0 +1,37 @@ +pack()); + } +} diff --git a/min/lib/Minify/Source.php b/min/lib/Minify/Source.php new file mode 100644 index 0000000..5a85d10 --- /dev/null +++ b/min/lib/Minify/Source.php @@ -0,0 +1,187 @@ + + */ +class Minify_Source { + + /** + * @var int time of last modification + */ + public $lastModified = null; + + /** + * @var callback minifier function specifically for this source. + */ + public $minifier = null; + + /** + * @var array minification options specific to this source. + */ + public $minifyOptions = null; + + /** + * @var string full path of file + */ + public $filepath = null; + + /** + * @var string HTTP Content Type (Minify requires one of the constants Minify::TYPE_*) + */ + public $contentType = null; + + /** + * Create a Minify_Source + * + * In the $spec array(), you can either provide a 'filepath' to an existing + * file (existence will not be checked!) or give 'id' (unique string for + * the content), 'content' (the string content) and 'lastModified' + * (unixtime of last update). + * + * As a shortcut, the controller will replace "//" at the beginning + * of a filepath with $_SERVER['DOCUMENT_ROOT'] . '/'. + * + * @param array $spec options + */ + public function __construct($spec) + { + if (isset($spec['filepath'])) { + if (0 === strpos($spec['filepath'], '//')) { + $spec['filepath'] = $_SERVER['DOCUMENT_ROOT'] . substr($spec['filepath'], 1); + } + $segments = explode('.', $spec['filepath']); + $ext = strtolower(array_pop($segments)); + switch ($ext) { + case 'js' : $this->contentType = 'application/x-javascript'; + break; + case 'css' : $this->contentType = 'text/css'; + break; + case 'htm' : // fallthrough + case 'html' : $this->contentType = 'text/html'; + break; + } + $this->filepath = $spec['filepath']; + $this->_id = $spec['filepath']; + $this->lastModified = filemtime($spec['filepath']) + // offset for Windows uploaders with out of sync clocks + + round(Minify::$uploaderHoursBehind * 3600); + } elseif (isset($spec['id'])) { + $this->_id = 'id::' . $spec['id']; + if (isset($spec['content'])) { + $this->_content = $spec['content']; + } else { + $this->_getContentFunc = $spec['getContentFunc']; + } + $this->lastModified = isset($spec['lastModified']) + ? $spec['lastModified'] + : time(); + } + if (isset($spec['contentType'])) { + $this->contentType = $spec['contentType']; + } + if (isset($spec['minifier'])) { + $this->minifier = $spec['minifier']; + } + if (isset($spec['minifyOptions'])) { + $this->minifyOptions = $spec['minifyOptions']; + } + } + + /** + * Get content + * + * @return string + */ + public function getContent() + { + $content = (null !== $this->filepath) + ? file_get_contents($this->filepath) + : ((null !== $this->_content) + ? $this->_content + : call_user_func($this->_getContentFunc, $this->_id) + ); + // remove UTF-8 BOM if present + return (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) + ? substr($content, 3) + : $content; + } + + /** + * Get id + * + * @return string + */ + public function getId() + { + return $this->_id; + } + + /** + * Verifies a single minification call can handle all sources + * + * @param array $sources Minify_Source instances + * + * @return bool true iff there no sources with specific minifier preferences. + */ + public static function haveNoMinifyPrefs($sources) + { + foreach ($sources as $source) { + if (null !== $source->minifier + || null !== $source->minifyOptions) { + return false; + } + } + return true; + } + + /** + * Get unique string for a set of sources + * + * @param array $sources Minify_Source instances + * + * @return string + */ + public static function getDigest($sources) + { + foreach ($sources as $source) { + $info[] = array( + $source->_id, $source->minifier, $source->minifyOptions + ); + } + return md5(serialize($info)); + } + + /** + * Get content type from a group of sources + * + * This is called if the user doesn't pass in a 'contentType' options + * + * @param array $sources Minify_Source instances + * + * @return string content type. e.g. 'text/css' + */ + public static function getContentType($sources) + { + foreach ($sources as $source) { + if ($source->contentType !== null) { + return $source->contentType; + } + } + return 'text/plain'; + } + + protected $_content = null; + protected $_getContentFunc = null; + protected $_id = null; +} + diff --git a/min/lib/Minify/YUICompressor.php b/min/lib/Minify/YUICompressor.php new file mode 100644 index 0000000..7cb61ad --- /dev/null +++ b/min/lib/Minify/YUICompressor.php @@ -0,0 +1,139 @@ + + * Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.3.5.jar'; + * Minify_YUICompressor::$tempDir = '/tmp'; + * $code = Minify_YUICompressor::minifyJs( + * $code + * ,array('nomunge' => true, 'line-break' => 1000) + * ); + * + * + * @todo unit tests, $options docs + * + * @package Minify + * @author Stephen Clay + */ +class Minify_YUICompressor { + + /** + * Filepath of the YUI Compressor jar file. This must be set before + * calling minifyJs() or minifyCss(). + * + * @var string + */ + public static $jarFile = null; + + /** + * Writable temp directory. This must be set before calling minifyJs() + * or minifyCss(). + * + * @var string + */ + public static $tempDir = null; + + /** + * Filepath of "java" executable (may be needed if not in shell's PATH) + * + * @var string + */ + public static $javaExecutable = 'java'; + + /** + * Minify a Javascript string + * + * @param string $js + * + * @param array $options (verbose is ignored) + * + * @see http://www.julienlecomte.net/yuicompressor/README + * + * @return string + */ + public static function minifyJs($js, $options = array()) + { + return self::_minify('js', $js, $options); + } + + /** + * Minify a CSS string + * + * @param string $css + * + * @param array $options (verbose is ignored) + * + * @see http://www.julienlecomte.net/yuicompressor/README + * + * @return string + */ + public static function minifyCss($css, $options = array()) + { + return self::_minify('css', $css, $options); + } + + private static function _minify($type, $content, $options) + { + self::_prepare(); + if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) { + throw new Exception('Minify_YUICompressor : could not create temp file.'); + } + file_put_contents($tmpFile, $content); + exec(self::_getCmd($options, $type, $tmpFile), $output); + unlink($tmpFile); + return implode("\n", $output); + } + + private static function _getCmd($userOptions, $type, $tmpFile) + { + $o = array_merge( + array( + 'charset' => '' + ,'line-break' => 5000 + ,'type' => $type + ,'nomunge' => false + ,'preserve-semi' => false + ,'disable-optimizations' => false + ) + ,$userOptions + ); + $cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile) + . " --type {$type}" + . (preg_match('/^[a-zA-Z\\-]+$/', $o['charset']) + ? " --charset {$o['charset']}" + : '') + . (is_numeric($o['line-break']) && $o['line-break'] >= 0 + ? ' --line-break ' . (int)$o['line-break'] + : ''); + if ($type === 'js') { + foreach (array('nomunge', 'preserve-semi', 'disable-optimizations') as $opt) { + $cmd .= $o[$opt] + ? " --{$opt}" + : ''; + } + } + return $cmd . ' ' . escapeshellarg($tmpFile); + } + + private static function _prepare() + { + if (! is_file(self::$jarFile) + || ! is_dir(self::$tempDir) + || ! is_writable(self::$tempDir) + ) { + throw new Exception('Minify_YUICompressor : $jarFile and $tempDir must be set.'); + } + } +} + diff --git a/min/lib/Solar/Dir.php b/min/lib/Solar/Dir.php new file mode 100644 index 0000000..37f7169 --- /dev/null +++ b/min/lib/Solar/Dir.php @@ -0,0 +1,199 @@ + + * + * @license http://opensource.org/licenses/bsd-license.php BSD + * + * @version $Id: Dir.php 2926 2007-11-09 16:25:44Z pmjones $ + * + */ +class Solar_Dir { + + /** + * + * The OS-specific temporary directory location. + * + * @var string + * + */ + protected static $_tmp; + + /** + * + * Hack for [[php::is_dir() | ]] that checks the include_path. + * + * Use this to see if a directory exists anywhere in the include_path. + * + * {{code: php + * $dir = Solar_Dir::exists('path/to/dir') + * if ($dir) { + * $files = scandir($dir); + * } else { + * echo "Not found in the include-path."; + * } + * }} + * + * @param string $dir Check for this directory in the include_path. + * + * @return mixed If the directory exists in the include_path, returns the + * absolute path; if not, returns boolean false. + * + */ + public static function exists($dir) + { + // no file requested? + $dir = trim($dir); + if (! $dir) { + return false; + } + + // using an absolute path for the file? + // dual check for Unix '/' and Windows '\', + // or Windows drive letter and a ':'. + $abs = ($dir[0] == '/' || $dir[0] == '\\' || $dir[1] == ':'); + if ($abs && is_dir($dir)) { + return $dir; + } + + // using a relative path on the file + $path = explode(PATH_SEPARATOR, ini_get('include_path')); + foreach ($path as $base) { + // strip Unix '/' and Windows '\' + $target = rtrim($base, '\\/') . DIRECTORY_SEPARATOR . $dir; + if (is_dir($target)) { + return $target; + } + } + + // never found it + return false; + } + + /** + * + * "Fixes" a directory string for the operating system. + * + * Use slashes anywhere you need a directory separator. Then run the + * string through fixdir() and the slashes will be converted to the + * proper separator (for example '\' on Windows). + * + * Always adds a final trailing separator. + * + * @param string $dir The directory string to 'fix'. + * + * @return string The "fixed" directory string. + * + */ + public static function fix($dir) + { + $dir = str_replace('/', DIRECTORY_SEPARATOR, $dir); + return rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + } + + /** + * + * Convenience method for dirname() and higher-level directories. + * + * @param string $file Get the dirname() of this file. + * + * @param int $up Move up in the directory structure this many + * times, default 0. + * + * @return string The dirname() of the file. + * + */ + public static function name($file, $up = 0) + { + $dir = dirname($file); + while ($up --) { + $dir = dirname($dir); + } + return $dir; + } + + /** + * + * Returns the OS-specific directory for temporary files. + * + * @param string $sub Add this subdirectory to the returned temporary + * directory name. + * + * @return string The temporary directory path. + * + */ + public static function tmp($sub = '') + { + // find the tmp dir if needed + if (! Solar_Dir::$_tmp) { + + // use the system if we can + if (function_exists('sys_get_temp_dir')) { + $tmp = sys_get_temp_dir(); + } else { + $tmp = Solar_Dir::_tmp(); + } + + // remove trailing separator and save + Solar_Dir::$_tmp = rtrim($tmp, DIRECTORY_SEPARATOR); + } + + // do we have a subdirectory request? + $sub = trim($sub); + if ($sub) { + // remove leading and trailing separators, and force exactly + // one trailing separator + $sub = trim($sub, DIRECTORY_SEPARATOR) + . DIRECTORY_SEPARATOR; + } + + return Solar_Dir::$_tmp . DIRECTORY_SEPARATOR . $sub; + } + + /** + * + * Returns the OS-specific temporary directory location. + * + * @return string The temp directory path. + * + */ + protected static function _tmp() + { + // non-Windows system? + if (strtolower(substr(PHP_OS, 0, 3)) != 'win') { + $tmp = empty($_ENV['TMPDIR']) ? getenv('TMPDIR') : $_ENV['TMPDIR']; + if ($tmp) { + return $tmp; + } else { + return '/tmp'; + } + } + + // Windows 'TEMP' + $tmp = empty($_ENV['TEMP']) ? getenv('TEMP') : $_ENV['TEMP']; + if ($tmp) { + return $tmp; + } + + // Windows 'TMP' + $tmp = empty($_ENV['TMP']) ? getenv('TMP') : $_ENV['TMP']; + if ($tmp) { + return $tmp; + } + + // Windows 'windir' + $tmp = empty($_ENV['windir']) ? getenv('windir') : $_ENV['windir']; + if ($tmp) { + return $tmp; + } + + // final fallback for Windows + return getenv('SystemRoot') . '\\temp'; + } +} \ No newline at end of file diff --git a/themes/jqt/img/activeButton.png b/themes/jqt/img/activeButton.png deleted file mode 100644 index a472f1b..0000000 Binary files a/themes/jqt/img/activeButton.png and /dev/null differ diff --git a/themes/jqt/img/back_button.png b/themes/jqt/img/back_button.png deleted file mode 100644 index 9873901..0000000 Binary files a/themes/jqt/img/back_button.png and /dev/null differ diff --git a/themes/jqt/img/back_button_clicked.png b/themes/jqt/img/back_button_clicked.png deleted file mode 100644 index 5ec4230..0000000 Binary files a/themes/jqt/img/back_button_clicked.png and /dev/null differ diff --git a/themes/jqt/img/blueButton.png b/themes/jqt/img/blueButton.png deleted file mode 100644 index 7c39f27..0000000 Binary files a/themes/jqt/img/blueButton.png and /dev/null differ diff --git a/themes/jqt/img/button.png b/themes/jqt/img/button.png deleted file mode 100644 index 52cc7e2..0000000 Binary files a/themes/jqt/img/button.png and /dev/null differ diff --git a/themes/jqt/img/button_clicked.png b/themes/jqt/img/button_clicked.png deleted file mode 100644 index 25d478f..0000000 Binary files a/themes/jqt/img/button_clicked.png and /dev/null differ diff --git a/themes/jqt/img/chevron.png b/themes/jqt/img/chevron.png deleted file mode 100644 index 5bdaa46..0000000 Binary files a/themes/jqt/img/chevron.png and /dev/null differ diff --git a/themes/jqt/img/chevron_circle.png b/themes/jqt/img/chevron_circle.png deleted file mode 100644 index b477e7c..0000000 Binary files a/themes/jqt/img/chevron_circle.png and /dev/null differ diff --git a/themes/jqt/img/grayButton.png b/themes/jqt/img/grayButton.png deleted file mode 100644 index 0ce6a30..0000000 Binary files a/themes/jqt/img/grayButton.png and /dev/null differ diff --git a/themes/jqt/img/greenButton.png b/themes/jqt/img/greenButton.png deleted file mode 100644 index a45594b..0000000 Binary files a/themes/jqt/img/greenButton.png and /dev/null differ diff --git a/themes/jqt/img/loading.gif b/themes/jqt/img/loading.gif deleted file mode 100644 index 2b4205b..0000000 Binary files a/themes/jqt/img/loading.gif and /dev/null differ diff --git a/themes/jqt/img/on_off.png b/themes/jqt/img/on_off.png deleted file mode 100644 index 1fed123..0000000 Binary files a/themes/jqt/img/on_off.png and /dev/null differ diff --git a/themes/jqt/img/redButton.png b/themes/jqt/img/redButton.png deleted file mode 100644 index f5728df..0000000 Binary files a/themes/jqt/img/redButton.png and /dev/null differ diff --git a/themes/jqt/img/rowhead.png b/themes/jqt/img/rowhead.png deleted file mode 100644 index b2fa8f6..0000000 Binary files a/themes/jqt/img/rowhead.png and /dev/null differ diff --git a/themes/jqt/img/toggle.png b/themes/jqt/img/toggle.png deleted file mode 100644 index 3b62ebf..0000000 Binary files a/themes/jqt/img/toggle.png and /dev/null differ diff --git a/themes/jqt/img/toggleOn.png b/themes/jqt/img/toggleOn.png deleted file mode 100644 index b016814..0000000 Binary files a/themes/jqt/img/toggleOn.png and /dev/null differ diff --git a/themes/jqt/img/toolbar.png b/themes/jqt/img/toolbar.png deleted file mode 100644 index c17bcf2..0000000 Binary files a/themes/jqt/img/toolbar.png and /dev/null differ diff --git a/themes/jqt/img/whiteButton.png b/themes/jqt/img/whiteButton.png deleted file mode 100644 index 5514b27..0000000 Binary files a/themes/jqt/img/whiteButton.png and /dev/null differ diff --git a/themes/jqt/theme.css b/themes/jqt/theme.css deleted file mode 100644 index 69344c4..0000000 --- a/themes/jqt/theme.css +++ /dev/null @@ -1,561 +0,0 @@ -body, #jqt { - background: #000; - color: #ddd; -} -body > *, div#jqt > * { - background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#333), to(#5e5e65)); -} -#jqt h1, #jqt h2 { - font: bold 18px "Helvetica Neue", Helvetica; - text-shadow: rgba(255,255,255,.2) 0 1px 1px; - color: #000; - margin: 10px 20px 5px; -} -/* @group Toolbar */ -#jqt .toolbar { - -webkit-box-sizing: border-box; - border-bottom: 1px solid #000; - padding: 8px; - height: 45px; - background: url(img/toolbar.png) #000000 repeat-x; - position: relative; -} -#jqt .black-translucent .toolbar { - margin-top: 20px; -} -div#jqt .toolbar > h1 { - position: absolute; - overflow: hidden; - left: 48%; - vertical-align:middle; - line-height: 1em; - margin: 1px 0 0 -75px; - height: 40px; - font-size: 18px; - width: 140px; - font-weight: bold; - text-shadow: rgba(0,0,0,1) 0 -1px 1px; - text-align: center; - text-overflow: ellipsis; - white-space: nowrap; - color: #fff; -} -div#jqt.landscape .toolbar > h1 { - margin-left: -125px; - width: 250px; -} -#jqt .button, #jqt .back, #jqt .cancel, #jqt .add { - position: absolute; - overflow: hidden; - top: 8px; - right: 10px; - margin: 0; - border-width: 0 5px; - padding: 0 3px; - width: auto; - height: 30px; - line-height: 30px; - font-family: inherit; - font-size: 12px; - font-weight: bold; - color: #fff; - text-shadow: rgba(0, 0, 0, 0.5) 0px -1px 0; - text-overflow: ellipsis; - text-decoration: none; - white-space: nowrap; - background: none; - -webkit-border-image: url(img/button.png) 0 5 0 5; -} -#jqt .button.active, #jqt .cancel.active, #jqt .add.active { - -webkit-border-image: url(img/button_clicked.png) 0 5 0 5; - color: #aaa; -} -#jqt .blueButton { - -webkit-border-image: url(img/blueButton.png) 0 5 0 5; - border-width: 0 5px; -} -#jqt .back { - left: 6px; - right: auto; - padding: 0; - max-width: 55px; - border-width: 0 8px 0 14px; - -webkit-border-image: url(img/back_button.png) 0 8 0 14; -} -#jqt .back.active { - -webkit-border-image: url(img/back_button_clicked.png) 0 8 0 14; - color: #aaa; -} -#jqt .leftButton, #jqt .cancel { - left: 6px; - right: auto; -} -#jqt .add { - font-size: 24px; - line-height: 24px; - font-weight: bold; -} -#jqt .whiteButton, -#jqt .grayButton, #jqt .redButton, #jqt .blueButton, #jqt .greenButton { - display: block; - border-width: 0 12px; - padding: 10px; - text-align: center; - font-size: 20px; - font-weight: bold; - text-decoration: inherit; - color: inherit; -} - -#jqt .whiteButton.active, #jqt .grayButton.active, #jqt .redButton.active, #jqt .blueButton.active, #jqt .greenButton.active, -#jqt .whiteButton:active, #jqt .grayButton:active, #jqt .redButton:active, #jqt .blueButton:active, #jqt .greenButton:active { - -webkit-border-image: url(img/activeButton.png) 0 12 0 12; -} -#jqt .whiteButton { - -webkit-border-image: url(img/whiteButton.png) 0 12 0 12; - text-shadow: rgba(255, 255, 255, 0.7) 0 1px 0; -} -#jqt .grayButton { - -webkit-border-image: url(img/grayButton.png) 0 12 0 12; - color: #FFFFFF; -} - -#jqt .redButton { - -webkit-border-image: url(img/redButton.png) 0 12 0 12; - color: #FFFFFF; -} - -#jqt .greenButton { - -webkit-border-image: url(img/greenButton.png) 0 12 0 12; - color: #FFFFFF; -} - - -/* @end */ -/* @group Lists */ -#jqt h1 + ul, #jqt h2 + ul, #jqt h3 + ul, #jqt h4 + ul, #jqt h5 + ul, #jqt h6 + ul { - margin-top: 0; -} -#jqt ul { - color: #aaa; - border: 1px solid #333333; - font: bold 18px "Helvetica Neue", Helvetica; - padding: 0; - margin: 15px 10px 17px 10px; -} -#jqt ul.rounded { - -webkit-border-radius: 8px; - -webkit-box-shadow: rgba(0,0,0,.3) 1px 1px 3px; -} -#jqt ul.rounded li:first-child, #jqt ul.rounded li:first-child a { - border-top: 0; - -webkit-border-top-left-radius: 8px; - -webkit-border-top-right-radius: 8px; -} -#jqt ul.rounded li:last-child, #jqt ul.rounded li:last-child a { - -webkit-border-bottom-left-radius: 8px; - -webkit-border-bottom-right-radius: 8px; -} -#jqt ul li { - color: #666; - border-top: 1px solid #333; - border-bottom: #555858; - list-style-type: none; - padding: 10px 10px 10px 10px; - background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#4c4d4e), to(#404142)); - overflow: hidden; -} -#jqt ul li.arrow { - background-image: url(img/chevron.png), -webkit-gradient(linear, 0% 0%, 0% 100%, from(#4c4d4e), to(#404142)); - background-position: right center; - background-repeat: no-repeat; -} - -#jqt ul li.arrow a { - padding: 12px 22px 12px 10px; -} - -#jqt ul li.forward { - background-image: url(img/chevron_circle.png), -webkit-gradient(linear, 0% 0%, 0% 100%, from(#4c4d4e), to(#404142)); - background-position: right center; - background-repeat: no-repeat; -} -/* universal links on list */ -#jqt ul li a, #jqt li.img a + a { - color: #fff; - text-decoration: none; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - display: block; - padding: 12px 10px 12px 10px; - margin: -10px; - -webkit-tap-highlight-color: rgba(0,0,0,0); - text-shadow: rgba(0,0,0,.2) 0 1px 1px; -} -#jqt ul li a.active, #jqt ul li a.button { - background-color: #ff8300; - color: #fff; -} -#jqt ul li a.active.loading { - background-image: url(img/loading.gif); - background-position: 95% center; - background-repeat: no-repeat; -} -#jqt ul li.arrow a.active { - background-image: url(img/chevron.png); - background-position: right center; - background-repeat: no-repeat; -} -#jqt ul li.forward a.active { - background-image: url(img/chevron_circle.png); - background-position: right center; - background-repeat: no-repeat; -} -#jqt ul li.img a + a { - margin: -10px 10px -20px -5px; - font-size: 17px; - font-weight: bold; -} -#jqt ul li.img a + a + a { - font-size: 14px; - font-weight: normal; - margin-left: -10px; - margin-bottom: -10px; - margin-top: 0; -} -#jqt ul li.img a + small + a { - margin-left: -5px; -} -#jqt ul li.img a + small + a + a { - margin-left: -10px; - margin-top: -20px; - margin-bottom: -10px; - font-size: 14px; - font-weight: normal; -} -#jqt ul li.img a + small + a + a + a { - margin-left: 0px !important; - margin-bottom: 0; -} -#jqt ul li a + a { - color: #000; - font: 14px "Helvetica Neue", Helvetica; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - display: block; - margin: 0; - padding: 0; -} -#jqt ul li a + a + a, #jqt ul li.img a + a + a + a, #jqt ul li.img a + small + a + a + a { - color: #666; - font: 13px "Helvetica Neue", Helvetica; - margin: 0; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - display: block; - padding: 0; -} -/* -@end */ -/* @group Forms */ -#jqt ul.form li { - padding: 7px 10px; -} -#jqt ul.form li.error { - border: 2px solid red; -} -#jqt ul.form li.error + li.error { - border-top: 0; -} -#jqt ul li input[type="text"], -#jqt ul li input[type="password"], -#jqt ul li input[type="tel"], -#jqt ul li input[type="number"], -#jqt ul li input[type="search"], -#jqt ul li input[type="email"], -#jqt ul li input[type="url"], -#jqt ul li textarea, -#jqt ul li select { - color: #FFFFFF; - background: transparent url('data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='); - border: 0; - font: bold 17px "Helvetica Neue", Helvetica; - padding: 0; - display: inline-block; - margin-left: 10px; - width: 100%; - -webkit-appearance: textarea; -} -#jqt ul li textarea { - height: 120px; - padding: 0; - text-indent: -2px; -} -#jqt ul li select { - text-indent: 15px; - background: transparent url(img/chevron.png) no-repeat right center; - -webkit-appearance: textfield; - margin-left: -6px; - width: 104%; -} -#jqt ul li input[type="checkbox"], #jqt ul li input[type="radio"] { - margin: 0; - padding: 10px 10px; -} -#jqt ul li input[type="checkbox"]:after, #jqt ul li input[type="radio"]:after { - content: attr(title); - font: 17px "Helvetica Neue", Helvetica; - display: block; - width: 246px; - color: #ff8300; - margin: -12px 0 0 17px; -} -/* @end */ -/* @group Mini Label */ -#jqt ul li small { - color: #64c114; - font: 17px "Helvetica Neue", Helvetica; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - display: block; - width: 23%; - float: right; - padding: 0; -} -#jqt ul li.arrow small { - padding: 0 15px; -} -#jqt ul li small.counter { - font-size: 17px; - line-height: 13px; - font-weight: bold; - background: rgba(0,0,0,.15); - color: #fff; - -webkit-border-radius: 11px; - padding: 4px 10px 5px 10px; - display: block; - width: auto; - margin-top: -25px; - -webkit-box-shadow: rgba(255,255,255,.1) 0 1px 0; -} -#jqt ul li.arrow small.counter { - margin-right: 15px; -} -/* @end */ -/* @group Individual */ -#jqt ul.individual { - border: 0; - background: none; - clear: both; - overflow: hidden; - padding-bottom: 3px; - -webkit-box-shadow: none; -} -#jqt ul.individual li { - background: #4c4d4e; - border: 1px solid #333; - font-size: 14px; - text-align: center; - -webkit-border-radius: 8px; - -webkit-box-sizing: border-box; - width: 48%; - float: left; - display: block; - padding: 11px 10px 14px 10px; - -webkit-box-shadow: rgba(0,0,0,.2) 1px 1px 3px; - background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#4c4d4e), to(#404142)); -} -#jqt ul.individual li + li { - float: right; -} -#jqt ul.individual li a { - color: #fff; - line-height: 16px; - margin: -11px -10px -14px -10px; - padding: 11px 10px 14px 10px; - -webkit-border-radius: 8px; -} -/* @end */ -/* @group Toggle */ -#jqt .toggle { - width: 94px; - position: relative; - height: 27px; - display: block; - overflow: hidden; - float: right; -} -#jqt .toggle input[type="checkbox"]:checked { - left: 0px; -} -#jqt .toggle input[type="checkbox"] { - -webkit-appearance: textarea; - -webkit-border-radius: 5px; - -webkit-tap-highlight-color: rgba(0,0,0,0); - -webkit-transition: left .15s; - background-color: transparent; - background: #fff url(img/on_off.png) 0 0 no-repeat; - border: 0; - height: 27px; - left: -55px; - margin: 0; - overflow: hidden; - position: absolute; - top: 0; - width: 149px; -} -/* @end */ -/* @group Info */ -#jqt .info { - background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#ccc), to(#aaa), color-stop(.6,#CCCCCC)); - font-size: 12px; - line-height: 16px; - text-align: center; - text-shadow: rgba(255,255,255,.8) 0 1px 0; - color: #444; - padding: 15px; - border-top: 1px solid rgba(255,255,255,.2); - font-weight: bold; -} -/* @end */ -/* @group Edge to edge */ -#jqt ul.edgetoedge { - border-width: 1px 0; - margin: 0; - padding: 0; -} -#jqt ul.edgetoedge li { - background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#1e1f21), to(#272729)); - border-bottom: 2px solid #000; - border-top: 1px solid #4a4b4d; - font-size: 20px; - margin-bottom: -1px; -} -#jqt ul.edgetoedge li.sep { - background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(0,0,0,0)), to(rgba(0,0,0,.5))); - border-bottom: 1px solid #111113; - border-top: 1px solid #666; - color: #3e9ac3; - font-size: 16px; - margin: 1px 0 0 0; - padding: 2px 10px; - text-shadow: #000 0 1px 0; -} -#jqt ul.edgetoedge li em { - font-weight: normal; - font-style: normal; -} -/* @end */ -/* @group Plastic */ -#jqt #plastic { - background: #17181a; -} -#jqt ul.plastic { - background: #17181a; - color: #aaa; - font: bold 18px "Helvetica Neue", Helvetica; - margin: 0; - padding: 0; - border-width: 0 0 1px 0; -} -#jqt ul.plastic li { - border-width: 1px 0; - border-style: solid; - border-top-color: #222; - border-bottom-color: #000; - color: #666; - list-style-type: none; - overflow: hidden; - padding: 10px 10px 10px 10px; -} -#jqt ul.plastic li a.active.loading { - background-image: url(img/loading.gif); - background-position: 95% center; - background-repeat: no-repeat; -} -#jqt ul.plastic li small { - color: #888; - font-size: 13px; - font-weight: bold; - line-height: 24px; - text-transform: uppercase; -} -#jqt ul.plastic li:nth-child(odd) { - background-color: #1c1c1f; -} -#jqt ul.plastic li.arrow { - background-image: url(img/chevron.png); - background-position: right center; - background-repeat: no-repeat; -} -#jqt ul.plastic li.arrow a.active { - background-image: url(img/chevron.png); - background-position: right center; - background-repeat: no-repeat; -} -#jqt ul.plastic li.forward { - background-image: url(img/chevron_circle.png); - background-position: right center; - background-repeat: no-repeat; -} -#jqt ul.plastic li.forward a.active { - background-image: url(img/chevron_circle.png); - background-position: right center; - background-repeat: no-repeat; -} -/* @group Metal */ -#jqt ul.metal { - border-bottom: 0; - border-left: 0; - border-right: 0; - border-top: 0; - margin: 0; -} -#jqt ul.metal li { - background-image: none; - border-top: 1px solid #fff; - border-bottom: 1px solid #666; - font-size: 26px; - background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(238,238,238,1)), to(rgba(156,158,160,1))); -} -#jqt ul.metal li a { - line-height: 26px; - margin: 0; - text-shadow: #fff 0 1px 0; - padding: 13px 0; -} -#jqt ul.metal li a em { - display: block; - font-size: 14px; - font-style: normal; - color: #444; - width: 50%; - line-height: 14px; -} -#jqt ul.metal li a.active { - color: rgb(0,0,0); -} -#jqt ul.metal li small { - float: right; - position: relative; - margin-top: 10px; - font-weight: bold; -} -#jqt ul.metal li.arrow { - background-image: url(img/chevron.png); - background-position: right center; - background-repeat: no-repeat; - background-image: url(img/chevron.png), -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(238,238,238,1)), to(rgba(156,158,160,1))); - background-repeat: no-repeat; - background-position: right center; -} -#jqt ul.metal li.arrow a small { - padding-right: 15px; - line-height: 17px; -} -/* @end */ diff --git a/themes/jqt/theme.min.css b/themes/jqt/theme.min.css deleted file mode 100644 index 0182b88..0000000 --- a/themes/jqt/theme.min.css +++ /dev/null @@ -1 +0,0 @@ -body{background:#000;color:#ddd;}body>*{background:-webkit-gradient(linear,0% 0,0% 100%,from(#333),to(#5e5e65));}h1,h2{font:bold 18px Helvetica;text-shadow:rgba(255,255,255,.2) 0 1px 1px;color:#000;margin:10px 20px 5px;}.toolbar{-webkit-box-sizing:border-box;border-bottom:1px solid #000;padding:10px;height:45px;background:url(img/toolbar.png) #000 repeat-x;position:relative;}.black-translucent .toolbar{margin-top:20px;}.toolbar>h1{position:absolute;overflow:hidden;left:50%;top:10px;line-height:1em;margin:1px 0 0 -75px;height:40px;font-size:20px;width:150px;font-weight:bold;text-shadow:rgba(0,0,0,1) 0 -1px 1px;text-align:center;text-overflow:ellipsis;white-space:nowrap;color:#fff;}body.landscape .toolbar>h1{margin-left:-125px;width:250px;}.button,.back,.cancel,.add{position:absolute;overflow:hidden;top:8px;right:10px;margin:0;border-width:0 5px;padding:0 3px;width:auto;height:30px;line-height:30px;font-family:inherit;font-size:12px;font-weight:bold;color:#fff;text-shadow:rgba(0,0,0,0.5) 0 -1px 0;text-overflow:ellipsis;text-decoration:none;white-space:nowrap;background:none;-webkit-border-image:url(img/button.png) 0 5 0 5;}.blueButton{-webkit-border-image:url(img/blueButton.png) 0 5 0 5;border-width:0 5px;}.back{left:6px;right:auto;padding:0;max-width:55px;border-width:0 8px 0 14px;-webkit-border-image:url(img/back_button.png) 0 8 0 14;}.back.active{-webkit-border-image:url(img/back_button_clicked.png) 0 8 0 14;color:#aaa;}.leftButton,.cancel{left:6px;right:auto;}.add{font-size:24px;line-height:24px;font-weight:bold;}.whiteButton,.grayButton{display:block;border-width:0 12px;padding:10px;text-align:center;font-size:20px;font-weight:bold;text-decoration:inherit;color:inherit;}.whiteButton{-webkit-border-image:url(img/whiteButton.png) 0 12 0 12;text-shadow:rgba(255,255,255,0.7) 0 1px 0;}.grayButton{-webkit-border-image:url(img/grayButton.png) 0 12 0 12;color:#FFF;}h1+ul,h2+ul,h3+ul,h4+ul,h5+ul,h6+ul{margin-top:0;}ul{color:#aaa;border:1px solid #333;font:bold 18px Helvetica;padding:0;margin:15px 10px 17px 10px;}ul.rounded{-webkit-border-radius:8px;-webkit-box-shadow:rgba(0,0,0,.3) 1px 1px 3px;}ul.rounded li:first-child,ul.rounded li:first-child a{border-top:0;-webkit-border-top-left-radius:8px;-webkit-border-top-right-radius:8px;}ul.rounded li:last-child,ul.rounded li:last-child a{-webkit-border-bottom-left-radius:8px;-webkit-border-bottom-right-radius:8px;}ul li{color:#666;border-top:1px solid #333;border-bottom:#555858;list-style-type:none;padding:10px 10px 10px 10px;background:-webkit-gradient(linear,0% 0,0% 100%,from(#4c4d4e),to(#404142));overflow:hidden;}ul li.arrow{background-image:url(img/chevron.png),-webkit-gradient(linear,0% 0,0% 100%,from(#4c4d4e),to(#404142));background-position:right center;background-repeat:no-repeat;}ul li.forward{background-image:url(img/chevron_circle.png),-webkit-gradient(linear,0% 0,0% 100%,from(#4c4d4e),to(#404142));background-position:right center;background-repeat:no-repeat;}ul li a,li.img a+a{color:#fff;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;padding:12px 10px 12px 10px;margin:-10px;-webkit-tap-highlight-color:rgba(0,0,0,0);text-shadow:rgba(0,0,0,.2) 0 1px 1px;}ul li a.active,ul li a.button{background-color:#53b401;color:#fff;}ul li a.active.loading{background-image:url(img/loading.gif);background-position:95% center;background-repeat:no-repeat;}ul li.arrow a.active{background-image:url(img/chevron.png);background-position:right center;background-repeat:no-repeat;}ul li.forward a.active{background-image:url(img/chevron_circle.png);background-position:right center;background-repeat:no-repeat;}ul li.img a+a{margin:-10px 10px -20px -5px;font-size:17px;font-weight:bold;}ul li.img a+a+a{font-size:14px;font-weight:normal;margin-left:-10px;margin-bottom:-10px;margin-top:0;}ul li.img a+small+a{margin-left:-5px;}ul li.img a+small+a+a{margin-left:-10px;margin-top:-20px;margin-bottom:-10px;font-size:14px;font-weight:normal;}ul li.img a+small+a+a+a{margin-left:0!important;margin-bottom:0;}ul li a+a{color:#000;font:14px Helvetica;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;margin:0;padding:0;}ul li a+a+a,ul li.img a+a+a+a,ul li.img a+small+a+a+a{color:#666;font:13px Helvetica;margin:0;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;padding:0;}ul.form li{padding:7px 10px;}ul.form li.error{border:2px solid red;}ul.form li.error+li.error{border-top:0;}ul li input[type="text"],ul li input[type="password"],ul li textarea,ul li select{color:#777;background:transparent url(../.png);border:0;font:normal 17px Helvetica;padding:0;display:inline-block;margin-left:0;width:100%;-webkit-appearance:textarea;}ul li textarea{height:120px;padding:0;text-indent:-2px;}ul li select{text-indent:0;background:transparent url(img/chevron.png) no-repeat right center;-webkit-appearance:textfield;margin-left:-6px;width:104%;}ul li input[type="checkbox"],ul li input[type="radio"]{margin:0;padding:10px 10px;}ul li input[type="checkbox"]:after,ul li input[type="radio"]:after{content:attr(title);font:17px Helvetica;display:block;width:246px;color:#777;margin:-12px 0 0 17px;}ul li small{color:#64c114;font:17px Helvetica;text-align:right;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;width:23%;float:right;padding:0;}ul li.arrow small{padding:0 15px;}ul li small.counter{font-size:17px;line-height:13px;font-weight:bold;background:rgba(0,0,0,.15);color:#fff;-webkit-border-radius:11px;padding:4px 10px 5px 10px;display:block;width:auto;margin-top:-22px;-webkit-box-shadow:rgba(255,255,255,.1) 0 1px 0;}ul li.arrow small.counter{margin-right:15px;}ul.individual{border:0;background:none;clear:both;overflow:hidden;padding-bottom:3px;-webkit-box-shadow:none;}ul.individual li{background:#4c4d4e;border:1px solid #333;font-size:14px;text-align:center;-webkit-border-radius:8px;-webkit-box-sizing:border-box;width:48%;float:left;display:block;padding:11px 10px 14px 10px;-webkit-box-shadow:rgba(0,0,0,.2) 1px 1px 3px;background:-webkit-gradient(linear,0% 0,0% 100%,from(#4c4d4e),to(#404142));}ul.individual li+li{float:right;}ul.individual li a{color:#fff;line-height:16px;margin:-11px -10px -14px -10px;padding:11px 10px 14px 10px;-webkit-border-radius:8px;}.toggle{width:94px;position:relative;height:27px;display:block;overflow:hidden;float:right;}.toggle input[type="checkbox"]:checked{left:0;}.toggle input[type="checkbox"]{-webkit-appearance:textarea;-webkit-border-radius:5px;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-transition:left .15s;background-color:transparent;background:#fff url(img/on_off.png) 0 0 no-repeat;border:0;height:27px;left:-55px;margin:0;overflow:hidden;position:absolute;top:0;width:149px;}.info{background:-webkit-gradient(linear,0% 0,0% 100%,from(#ccc),to(#aaa),color-stop(.6,#CCC));font-size:12px;line-height:16px;text-align:center;text-shadow:rgba(255,255,255,.8) 0 1px 0;color:#444;padding:15px;border-top:1px solid rgba(255,255,255,.2);font-weight:bold;}ul.edgetoedge{border-width:1px 0;margin:0;padding:0;}ul.edgetoedge li{background:-webkit-gradient(linear,0% 0,0% 100%,from(#1e1f21),to(#272729));border-bottom:2px solid #000;border-top:1px solid #4a4b4d;font-size:20px;margin-bottom:-1px;}ul.edgetoedge li.sep{background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(0,0,0,0)),to(rgba(0,0,0,.5)));border-bottom:1px solid #111113;border-top:1px solid #666;color:#3e9ac3;font-size:16px;margin:1px 0 0 0;padding:2px 10px;text-shadow:#000 0 1px 0;}ul.edgetoedge li em{font-weight:normal;font-style:normal;}#plastic{background:#17181a;}ul.plastic{background:#17181a;color:#aaa;font:bold 18px Helvetica;margin:0;padding:0;border-width:0 0 1px 0;}ul.plastic li{border-width:1px 0;border-style:solid;border-top-color:#222;border-bottom-color:#000;color:#666;list-style-type:none;overflow:hidden;padding:10px 10px 10px 10px;}ul.plastic li a.active.loading{background-image:url(img/loading.gif);background-position:95% center;background-repeat:no-repeat;}ul.plastic li small{color:#888;font-size:13px;font-weight:bold;line-height:24px;text-transform:uppercase;}ul.plastic li:nth-child(odd){background-color:#1c1c1f;}ul.plastic li.arrow{background-image:url(img/chevron.png);background-position:right center;background-repeat:no-repeat;}ul.plastic li.arrow a.active{background-image:url(img/chevron.png);background-position:right center;background-repeat:no-repeat;}ul.plastic li.forward{background-image:url(img/chevron_circle.png);background-position:right center;background-repeat:no-repeat;}ul.plastic li.forward a.active{background-image:url(img/chevron_circle.png);background-position:right center;background-repeat:no-repeat;}ul.metal{border-bottom:0;border-left:0;border-right:0;border-top:0;margin:0;}ul.metal li{background-image:none;border-top:1px solid #fff;border-bottom:1px solid #666;font-size:26px;background:-webkit-gradient(linear,0% 0,0% 100%,from(rgba(238,238,238,1)),to(rgba(156,158,160,1)));}ul.metal li a{line-height:26px;margin:0;text-shadow:#fff 0 1px 0;padding:13px 0;}ul.metal li a em{display:block;font-size:14px;font-style:normal;color:#444;width:50%;line-height:14px;}ul.metal li a.active{color:#000;}ul.metal li small{float:right;position:relative;margin-top:10px;font-weight:bold;}ul.metal li.arrow{background-image:url(img/chevron.png);background-position:right center;background-repeat:no-repeat;background-image:url(img/chevron.png),-webkit-gradient(linear,0% 0,0% 100%,from(rgba(238,238,238,1)),to(rgba(156,158,160,1)));background-repeat:no-repeat;background-position:right center;}ul.metal li.arrow a small{padding-right:15px;line-height:17px;} \ No newline at end of file -- cgit v1.2.3