From 9bf18c2ceb2582b938e80b45742c34ea5968cc99 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 5 Jun 2014 11:46:00 -0500 Subject: [PATCH 001/137] test($response-monitoring): return custom data w/ each response, and log it --- test/fineuploader/UploadReceiver.java | 2 ++ test/uploader-demo-jquery.js | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/test/fineuploader/UploadReceiver.java b/test/fineuploader/UploadReceiver.java index 0d3d7fc21..65e2bfbf6 100644 --- a/test/fineuploader/UploadReceiver.java +++ b/test/fineuploader/UploadReceiver.java @@ -99,6 +99,7 @@ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOEx assertCombinedFileIsVaid(requestParser.getTotalFileSize(), outputFile, requestParser.getUuid()); deletePartitionFiles(dir, requestParser.getUuid()); + writeResponse(resp.getWriter(), requestParser.generateError() ? "Generated error" : null, isIframe, false, requestParser, outputFile); } else if (ServletFileUpload.isMultipartContent(req)) { @@ -296,6 +297,7 @@ private void writeResponse(PrintWriter writer, String failureReason, boolean isI response.addProperty("success", true); + response.addProperty("foo", "bar"); if (file != null) { response.addProperty("thumbnailUrl", "/" + file.getPath()); diff --git a/test/uploader-demo-jquery.js b/test/uploader-demo-jquery.js index 7b224d54e..5673c1d79 100644 --- a/test/uploader-demo-jquery.js +++ b/test/uploader-demo-jquery.js @@ -130,7 +130,11 @@ }) .on("totalProgress", function(event, loaded, total) { // qq.log(loaded + "/" + total); - }); + }) + .on("complete", function(event, id, name, response, xhr) { + qq.log(response); + }); + $('#manual-example').fineUploader({ autoUpload: false, @@ -155,7 +159,9 @@ concurrent: { enabled: true }, - successEndpoint: "/upload/receiver?done" + success: { + endpoint: "/upload/receiver?done" + } }, resume: { enabled: true @@ -178,7 +184,11 @@ }) .on("progress", function(event, id, name, loaded, total) { // qq.log(loaded + "/" + total); - }); + }) + .on("complete", function(event, id, name, response, xhr) { + qq.log(response); + }); + $('#triggerUpload').click(function() { return $('#manual-example').fineUploader("uploadStoredFiles"); }); From 506b7f192c386721e6f6692517942b8032c80d3c Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 5 Jun 2014 11:51:08 -0500 Subject: [PATCH 002/137] fix($onComplete-response): onComplete callback omits server response fixes #1227 --- .../s3/multipart.complete.ajax.requester.js | 2 +- .../traditional.xhr.upload.handler.js | 12 +++++---- .../upload.handler.controller.js | 4 +-- .../js/upload-handler/xhr.upload.handler.js | 11 ++++++-- test/unit/chunked-uploads.js | 25 ++++++++++++------- test/unit/concurrent-chunks.js | 8 ++++-- 6 files changed, 41 insertions(+), 21 deletions(-) diff --git a/client/js/s3/multipart.complete.ajax.requester.js b/client/js/s3/multipart.complete.ajax.requester.js index 79567118e..e4a332810 100644 --- a/client/js/s3/multipart.complete.ajax.requester.js +++ b/client/js/s3/multipart.complete.ajax.requester.js @@ -106,7 +106,7 @@ qq.s3.CompleteMultipartAjaxRequester = function(o) { promise.failure("Problem asking Amazon to combine the parts!", xhr); } else { - promise.success(xhr); + promise.success({}, xhr); } } diff --git a/client/js/traditional/traditional.xhr.upload.handler.js b/client/js/traditional/traditional.xhr.upload.handler.js index 69823fc77..46fcb5471 100644 --- a/client/js/traditional/traditional.xhr.upload.handler.js +++ b/client/js/traditional/traditional.xhr.upload.handler.js @@ -85,7 +85,7 @@ qq.traditional.XhrUploadHandler = function(spec, proxy) { log("xhr - server response received for " + id); log("responseText = " + xhr.responseText); - response = parseResponse(xhr, true); + response = parseResponse(true, xhr); return { success: !isErrorUploadResponse(xhr, response), @@ -94,7 +94,7 @@ qq.traditional.XhrUploadHandler = function(spec, proxy) { }, // If this is an upload response, we require a JSON payload, otherwise, it is optional. - parseResponse = function(xhr, upload) { + parseResponse = function(upload, xhr) { var response = {}; try { @@ -118,9 +118,9 @@ qq.traditional.XhrUploadHandler = function(spec, proxy) { spec.customHeaders.get(id) ) .then(function(xhr) { - promise.success(parseResponse(xhr), xhr); + promise.success(parseResponse(false, xhr), xhr); }, function(xhr) { - promise.failure(parseResponse(xhr), xhr); + promise.failure(parseResponse(false, xhr), xhr); }); return promise; @@ -237,7 +237,9 @@ qq.traditional.XhrUploadHandler = function(spec, proxy) { if (spec.chunking.success.endpoint) { return sendChunksCompleteRequest(id); } - return super_.finalizeChunks(id); + else { + return super_.finalizeChunks(id, qq.bind(parseResponse, this, true)); + } } }; }); diff --git a/client/js/upload-handler/upload.handler.controller.js b/client/js/upload-handler/upload.handler.controller.js index 277d632ee..3acd256b5 100644 --- a/client/js/upload-handler/upload.handler.controller.js +++ b/client/js/upload-handler/upload.handler.controller.js @@ -62,8 +62,8 @@ qq.UploadHandlerController = function(o, namespace) { log("All chunks have been uploaded for " + id + " - finalizing...."); handler.finalizeChunks(id).then( - function(xhr) { - var normaizedResponse = upload.normalizeResponse({}, true); + function(response, xhr) { + var normaizedResponse = upload.normalizeResponse(response, true); options.onProgress(id, name, size, size); handler._maybeDeletePersistedChunkData(id); diff --git a/client/js/upload-handler/xhr.upload.handler.js b/client/js/upload-handler/xhr.upload.handler.js index c60de5ad0..4e67723ef 100644 --- a/client/js/upload-handler/xhr.upload.handler.js +++ b/client/js/upload-handler/xhr.upload.handler.js @@ -86,8 +86,15 @@ qq.XhrUploadHandler = function(spec) { // Called when all chunks have been successfully uploaded. Expected promissory return type. // This defines the default behavior if nothing further is required when all chunks have been uploaded. - finalizeChunks: function(id) { - return new qq.Promise().success(handler._getXhr(id)); + finalizeChunks: function(id, responseParser) { + var lastChunkIdx = handler._getTotalChunks(id) - 1, + xhr = handler._getXhr(id, lastChunkIdx); + + if (responseParser) { + return new qq.Promise().success(responseParser(xhr), xhr); + } + + return new qq.Promise().success({}, xhr); }, getFile: function(id) { diff --git a/test/unit/chunked-uploads.js b/test/unit/chunked-uploads.js index a610144b5..313fa00e3 100644 --- a/test/unit/chunked-uploads.js +++ b/test/unit/chunked-uploads.js @@ -23,7 +23,7 @@ if (qqtest.canDownloadFileAsBlob) { customParams = customParams || {}; chunkingParamNames = chunkingParamNames || new qq.FineUploaderBasic({})._options.chunking.paramNames; - assert.expect(2 + (expectedChunks * (18 + (Object.keys(customParams).length))), done); + assert.expect(3 + (expectedChunks * (20 + (Object.keys(customParams).length))), done); var uploader = new qq.FineUploaderBasic({ request: { @@ -50,7 +50,7 @@ if (qqtest.canDownloadFileAsBlob) { setTimeout(function () { var request = fileTestHelper.getRequests()[fileTestHelper.getRequests().length - 1]; - request.respond(200, null, JSON.stringify({success: true})); + request.respond(200, null, JSON.stringify({success: true, testParam: "testVal"})); }, 10); }, onUploadChunkSuccess: function (id, chunkData, response, xhr) { @@ -83,10 +83,14 @@ if (qqtest.canDownloadFileAsBlob) { assert.equal(chunkData.startByte, (chunksSent - 1) * chunkSize + 1, "Wrong startByte passed to onUploadChunkSuccess"); assert.equal(chunkData.endByte, chunksSucceeded === expectedChunks ? expectedFileSize : chunkData.startByte + chunkSize-1, "Wrong startByte passed to onUploadChunk"); assert.equal(chunkData.totalParts, expectedChunks, "Wrong totalParts passed to onUploadChunkSuccess"); + + assert.equal(response.testParam, "testVal"); + assert.ok(xhr); }, - onComplete: function (id) { + onComplete: function (id, name, response) { assert.equal(expectedChunks, chunksSent, "Wrong # of chunks sent."); assert.equal(expectedChunks, chunksSucceeded, "Wrong # of chunks succeeded"); + assert.equal(response.testParam, "testVal"); } } }), @@ -101,10 +105,10 @@ if (qqtest.canDownloadFileAsBlob) { function testChunkedFailureAndRecovery(restartAfterFailure, done) { if (restartAfterFailure) { - assert.expect(5 + (expectedChunks * 16) + (14 * (expectedChunks-1)), done); + assert.expect(6 + (expectedChunks * 17) + (15 * (expectedChunks-1)), done); } else { - assert.expect(5 + (expectedChunks * 16), done); + assert.expect(6 + (expectedChunks * 17), done); } var alreadyFailed = false, @@ -140,15 +144,15 @@ if (qqtest.canDownloadFileAsBlob) { if (restartAfterFailure) { chunksSent = 0; chunksSucceeded = 0; - request.respond(500, null, JSON.stringify({reset: true})); + request.respond(500, null, JSON.stringify({reset: true, testParam: "testVal"})); } else { chunksSent--; - request.respond(500, null); + request.respond(500, null, JSON.stringify({testParam: "testVal"})); } } else { - request.respond(200, null, JSON.stringify({success: true})); + request.respond(200, null, JSON.stringify({success: true, testParam: "testVal"})); } }, 10); }, @@ -171,10 +175,13 @@ if (qqtest.canDownloadFileAsBlob) { assert.equal(requestParams.qqfilename, uploader.getName(id), "Wrong filename param"); assert.equal(requestParams.qqchunksize, requestParams.qqfile.size, "Wrong chunk size param"); assert.equal(id, 0, "Wrong ID passed to onUpoadChunkSuccess"); + + assert.equal(response.testParam, "testVal"); }, - onComplete: function (id) { + onComplete: function (id, name, response) { assert.equal(expectedChunks, chunksSent, "Wrong # of chunks sent."); assert.equal(expectedChunks, chunksSucceeded, "Wrong # of chunks succeeded"); + assert.equal(response.testParam, "testVal"); } } }), diff --git a/test/unit/concurrent-chunks.js b/test/unit/concurrent-chunks.js index 5f8bc06e7..71839117b 100644 --- a/test/unit/concurrent-chunks.js +++ b/test/unit/concurrent-chunks.js @@ -14,7 +14,7 @@ if (qqtest.canDownloadFileAsBlob) { qq.each(fileTestHelper.getRequests(), function(idx, req) { if (!req.ack && (!endpoint || endpoint === req.url)) { req.ack = true; - req.respond(200, null, JSON.stringify({success: true})); + req.respond(200, null, JSON.stringify({success: true, testParam: "testVal"})); } }); }, 10); @@ -47,12 +47,16 @@ if (qqtest.canDownloadFileAsBlob) { chunksStarted++; acknowledgeRequests(); }, - onUploadChunkSuccess: function(id, chunkData) { + onUploadChunkSuccess: function(id, chunkData, response) { chunksStarted < expectedChunks && actualUploadsPerGroup.push(0); + assert.equal(response.testParam, "testVal"); }, onAllComplete: function(succeeded, failed) { assert.deepEqual(actualUploadsPerGroup, expectedUploadPerGroup); done(); + }, + onComplete: function(id, name, response) { + assert.equal(response.testParam, "testVal"); } } }); From 178f6bb150971d6dc8e572c1cce5de2cfa45b348 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 5 Jun 2014 11:52:11 -0500 Subject: [PATCH 003/137] chore($release): prepare for 5.0.1 release [skip ci] #1227 --- README.md | 2 +- bower.json | 2 +- client/js/version.js | 2 +- fineuploader.jquery.json | 2 +- package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index db3f02e66..ed7c15c00 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Fine Uploader](http://fineuploader.com/img/FineUploader_logo.png)](http://fineuploader.com/) -Version: 5.0.0 (IN PROGRESS) +Version: 5.0.1 (IN PROGRESS) [![Build Status](https://travis-ci.org/Widen/fine-uploader.png?branch=master)](https://travis-ci.org/Widen/fine-uploader) | [![Semver badge](http://calm-shore-6115.herokuapp.com/?label=SemVer&value=2.0.0&color=green)](http://semver.org/spec/v2.0.0.html) diff --git a/bower.json b/bower.json index 02aec6622..ba91bf189 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "fine-uploader", - "version": "5.0.0", + "version": "5.0.1", "devDependencies": { "jquery": "1.10.0", "purl": "https://github.com/allmarkedup/purl.git#~2.3.1", diff --git a/client/js/version.js b/client/js/version.js index b2cf1c86d..89afb6f31 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version="5.0.0"; +qq.version="5.0.1"; diff --git a/fineuploader.jquery.json b/fineuploader.jquery.json index a60ba7d0a..ca25acbb7 100644 --- a/fineuploader.jquery.json +++ b/fineuploader.jquery.json @@ -16,7 +16,7 @@ "azure", "microsoft" ], - "version": "5.0.0", + "version": "5.0.1", "author": { "name": "Widen Enterprises", "url": "http://widen.com" diff --git a/package.json b/package.json index 8f5180fd4..6b1bea7e5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fineuploader", "title": "Fine Uploader", - "version": "5.0.0", + "version": "5.0.1", "description": "Multiple file upload component with progress-bar, drag-and-drop, support for all modern browsers.", "main": "./fine-uploader/js/fineuploader.js", "maintainers": [ From 5ce381155366e1064fe3914aa8b691d23d3ee0f3 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 5 Jun 2014 11:55:28 -0500 Subject: [PATCH 004/137] chore($logging): add "finalize success" debug log message #1227 --- client/js/upload-handler/upload.handler.controller.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/js/upload-handler/upload.handler.controller.js b/client/js/upload-handler/upload.handler.controller.js index 3acd256b5..2c498330f 100644 --- a/client/js/upload-handler/upload.handler.controller.js +++ b/client/js/upload-handler/upload.handler.controller.js @@ -63,6 +63,8 @@ qq.UploadHandlerController = function(o, namespace) { log("All chunks have been uploaded for " + id + " - finalizing...."); handler.finalizeChunks(id).then( function(response, xhr) { + log("Finalize successful for " + id); + var normaizedResponse = upload.normalizeResponse(response, true); options.onProgress(id, name, size, size); From ac2ae5fe5ce67b688e9538eea263365d6e9f847f Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Mon, 9 Jun 2014 14:58:32 -0500 Subject: [PATCH 005/137] refactor(megapix-image.js): JSHint fixes, update comments --- client/js/third-party/megapix-image.js | 478 +++++++++++++------------ 1 file changed, 240 insertions(+), 238 deletions(-) diff --git a/client/js/third-party/megapix-image.js b/client/js/third-party/megapix-image.js index 6db0224e4..5df9c4d66 100644 --- a/client/js/third-party/megapix-image.js +++ b/client/js/third-party/megapix-image.js @@ -1,273 +1,275 @@ /*global qq, define */ /*jshint strict:false,bitwise:false,nonew:false,asi:true,-W064,-W116,-W089 */ /** - * Mega pixel image rendering library for iOS6 Safari + * Mega pixel image rendering library for iOS6+ * - * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel), + * Fixes iOS6+'s image file rendering issue for large size image (over mega-pixel), * which causes unexpected subsampling when drawing it in canvas. * By using this library, you can safely render the image with proper stretching. * * Copyright (c) 2012 Shinichi Tomita * Released under the MIT license + * + * Modified by Widen for Fine Uploader */ (function() { - /** - * Detect subsampling in loaded image. - * In iOS, larger images than 2M pixels may be subsampled in rendering. - */ - function detectSubsampling(img) { - var iw = img.naturalWidth, ih = img.naturalHeight; - if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image - var canvas = document.createElement('canvas'); - canvas.width = canvas.height = 1; - var ctx = canvas.getContext('2d'); - ctx.drawImage(img, -iw + 1, 0); - // subsampled image becomes half smaller in rendering size. - // check alpha channel value to confirm image is covering edge pixel or not. - // if alpha value is 0 image is not covering, hence subsampled. - return ctx.getImageData(0, 0, 1, 1).data[3] === 0; - } else { - return false; + /** + * Detect subsampling in loaded image. + * In iOS, larger images than 2M pixels may be subsampled in rendering. + */ + function detectSubsampling(img) { + var iw = img.naturalWidth, ih = img.naturalHeight; + if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image + var canvas = document.createElement("canvas"); + canvas.width = canvas.height = 1; + var ctx = canvas.getContext("2d"); + ctx.drawImage(img, -iw + 1, 0); + // subsampled image becomes half smaller in rendering size. + // check alpha channel value to confirm image is covering edge pixel or not. + // if alpha value is 0 image is not covering, hence subsampled. + return ctx.getImageData(0, 0, 1, 1).data[3] === 0; + } else { + return false; + } } - } - /** - * Detecting vertical squash in loaded image. - * Fixes a bug which squash image vertically while drawing into canvas for some images. - */ - function detectVerticalSquash(img, iw, ih) { - var canvas = document.createElement('canvas'); - canvas.width = 1; - canvas.height = ih; - var ctx = canvas.getContext('2d'); - ctx.drawImage(img, 0, 0); - var data = ctx.getImageData(0, 0, 1, ih).data; - // search image edge pixel position in case it is squashed vertically. - var sy = 0; - var ey = ih; - var py = ih; - while (py > sy) { - var alpha = data[(py - 1) * 4 + 3]; - if (alpha === 0) { - ey = py; - } else { - sy = py; - } - py = (ey + sy) >> 1; + /** + * Detecting vertical squash in loaded image. + * Fixes a bug which squash image vertically while drawing into canvas for some images. + */ + function detectVerticalSquash(img, iw, ih) { + var canvas = document.createElement("canvas"); + canvas.width = 1; + canvas.height = ih; + var ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0); + var data = ctx.getImageData(0, 0, 1, ih).data; + // search image edge pixel position in case it is squashed vertically. + var sy = 0; + var ey = ih; + var py = ih; + while (py > sy) { + var alpha = data[(py - 1) * 4 + 3]; + if (alpha === 0) { + ey = py; + } else { + sy = py; + } + py = (ey + sy) >> 1; + } + var ratio = (py / ih); + return (ratio===0)?1:ratio; } - var ratio = (py / ih); - return (ratio===0)?1:ratio; - } - /** - * Rendering image element (with resizing) and get its data URL - */ - function renderImageToDataURL(img, options, doSquash) { - var canvas = document.createElement('canvas'), - mime = options.mime || "image/jpeg"; + /** + * Rendering image element (with resizing) and get its data URL + */ + function renderImageToDataURL(img, options, doSquash) { + var canvas = document.createElement("canvas"), + mime = options.mime || "image/jpeg"; - renderImageToCanvas(img, canvas, options, doSquash); - return canvas.toDataURL(mime, options.quality || 0.8); - } + renderImageToCanvas(img, canvas, options, doSquash); + return canvas.toDataURL(mime, options.quality || 0.8); + } - /** - * Rendering image element (with resizing) into the canvas element - */ - function renderImageToCanvas(img, canvas, options, doSquash) { - var iw = img.naturalWidth, ih = img.naturalHeight; - var width = options.width, height = options.height; - var ctx = canvas.getContext('2d'); - ctx.save(); - transformCoordinate(canvas, width, height, options.orientation); + /** + * Rendering image element (with resizing) into the canvas element + */ + function renderImageToCanvas(img, canvas, options, doSquash) { + var iw = img.naturalWidth, ih = img.naturalHeight; + var width = options.width, height = options.height; + var ctx = canvas.getContext("2d"); + ctx.save(); + transformCoordinate(canvas, width, height, options.orientation); - // Fine Uploader specific: Save some CPU cycles if not using iOS - // Assumption: This logic is only needed to overcome iOS image sampling issues - if (qq.ios()) { - var subsampled = detectSubsampling(img); - if (subsampled) { - iw /= 2; - ih /= 2; + // Fine Uploader specific: Save some CPU cycles if not using iOS + // Assumption: This logic is only needed to overcome iOS image sampling issues + if (qq.ios()) { + var subsampled = detectSubsampling(img); + if (subsampled) { + iw /= 2; + ih /= 2; + } + var d = 1024; // size of tiling canvas + var tmpCanvas = document.createElement("canvas"); + tmpCanvas.width = tmpCanvas.height = d; + var tmpCtx = tmpCanvas.getContext("2d"); + var vertSquashRatio = doSquash ? detectVerticalSquash(img, iw, ih) : 1; + var dw = Math.ceil(d * width / iw); + var dh = Math.ceil(d * height / ih / vertSquashRatio); + var sy = 0; + var dy = 0; + while (sy < ih) { + var sx = 0; + var dx = 0; + while (sx < iw) { + tmpCtx.clearRect(0, 0, d, d); + tmpCtx.drawImage(img, -sx, -sy); + ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh); + sx += d; + dx += dw; + } + sy += d; + dy += dh; + } + ctx.restore(); + tmpCanvas = tmpCtx = null; } - var d = 1024; // size of tiling canvas - var tmpCanvas = document.createElement('canvas'); - tmpCanvas.width = tmpCanvas.height = d; - var tmpCtx = tmpCanvas.getContext('2d'); - var vertSquashRatio = doSquash ? detectVerticalSquash(img, iw, ih) : 1; - var dw = Math.ceil(d * width / iw); - var dh = Math.ceil(d * height / ih / vertSquashRatio); - var sy = 0; - var dy = 0; - while (sy < ih) { - var sx = 0; - var dx = 0; - while (sx < iw) { - tmpCtx.clearRect(0, 0, d, d); - tmpCtx.drawImage(img, -sx, -sy); - ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh); - sx += d; - dx += dw; - } - sy += d; - dy += dh; + else { + ctx.drawImage(img, 0, 0, width, height); } - ctx.restore(); - tmpCanvas = tmpCtx = null; - } - else { - ctx.drawImage(img, 0, 0, width, height); - } - canvas.qqImageRendered && canvas.qqImageRendered(); - } - - /** - * Transform canvas coordination according to specified frame size and orientation - * Orientation value is from EXIF tag - */ - function transformCoordinate(canvas, width, height, orientation) { - switch (orientation) { - case 5: - case 6: - case 7: - case 8: - canvas.width = height; - canvas.height = width; - break; - default: - canvas.width = width; - canvas.height = height; + canvas.qqImageRendered && canvas.qqImageRendered(); } - var ctx = canvas.getContext('2d'); - switch (orientation) { - case 2: - // horizontal flip - ctx.translate(width, 0); - ctx.scale(-1, 1); - break; - case 3: - // 180 rotate left - ctx.translate(width, height); - ctx.rotate(Math.PI); - break; - case 4: - // vertical flip - ctx.translate(0, height); - ctx.scale(1, -1); - break; - case 5: - // vertical flip + 90 rotate right - ctx.rotate(0.5 * Math.PI); - ctx.scale(1, -1); - break; - case 6: - // 90 rotate right - ctx.rotate(0.5 * Math.PI); - ctx.translate(0, -height); - break; - case 7: - // horizontal flip + 90 rotate right - ctx.rotate(0.5 * Math.PI); - ctx.translate(width, -height); - ctx.scale(-1, 1); - break; - case 8: - // 90 rotate left - ctx.rotate(-0.5 * Math.PI); - ctx.translate(-width, 0); - break; - default: - break; + + /** + * Transform canvas coordination according to specified frame size and orientation + * Orientation value is from EXIF tag + */ + function transformCoordinate(canvas, width, height, orientation) { + switch (orientation) { + case 5: + case 6: + case 7: + case 8: + canvas.width = height; + canvas.height = width; + break; + default: + canvas.width = width; + canvas.height = height; + } + var ctx = canvas.getContext("2d"); + switch (orientation) { + case 2: + // horizontal flip + ctx.translate(width, 0); + ctx.scale(-1, 1); + break; + case 3: + // 180 rotate left + ctx.translate(width, height); + ctx.rotate(Math.PI); + break; + case 4: + // vertical flip + ctx.translate(0, height); + ctx.scale(1, -1); + break; + case 5: + // vertical flip + 90 rotate right + ctx.rotate(0.5 * Math.PI); + ctx.scale(1, -1); + break; + case 6: + // 90 rotate right + ctx.rotate(0.5 * Math.PI); + ctx.translate(0, -height); + break; + case 7: + // horizontal flip + 90 rotate right + ctx.rotate(0.5 * Math.PI); + ctx.translate(width, -height); + ctx.scale(-1, 1); + break; + case 8: + // 90 rotate left + ctx.rotate(-0.5 * Math.PI); + ctx.translate(-width, 0); + break; + default: + break; + } } - } - /** - * MegaPixImage class - */ - function MegaPixImage(srcImage, errorCallback) { - if (window.Blob && srcImage instanceof Blob) { - var img = new Image(); - var URL = window.URL && window.URL.createObjectURL ? window.URL : - window.webkitURL && window.webkitURL.createObjectURL ? window.webkitURL : + /** + * MegaPixImage class + */ + function MegaPixImage(srcImage, errorCallback) { + if (window.Blob && srcImage instanceof Blob) { + var img = new Image(); + var URL = window.URL && window.URL.createObjectURL ? window.URL : + window.webkitURL && window.webkitURL.createObjectURL ? window.webkitURL : null; - if (!URL) { throw Error("No createObjectURL function found to create blob url"); } - img.src = URL.createObjectURL(srcImage); - this.blob = srcImage; - srcImage = img; - } - if (!srcImage.naturalWidth && !srcImage.naturalHeight) { - var _this = this; - srcImage.onload = function() { - var listeners = _this.imageLoadListeners; - if (listeners) { - _this.imageLoadListeners = null; - // IE11 doesn't reliably report actual image dimensions immediately after onload for small files, - // so let's push this to the end of the UI thread queue. - setTimeout(function() { - for (var i=0, len=listeners.length; i maxWidth) { - width = maxWidth; - height = (imgHeight * width / imgWidth) << 0; - } - if (maxHeight && height > maxHeight) { - height = maxHeight; - width = (imgWidth * height / imgHeight) << 0; - } - var opt = { width : width, height : height }; - for (var k in options) opt[k] = options[k]; + /** + * Rendering megapix image into specified target element + */ + MegaPixImage.prototype.render = function(target, options) { + if (this.imageLoadListeners) { + var _this = this; + this.imageLoadListeners.push(function() { _this.render(target, options) }); + return; + } + options = options || {}; + var imgWidth = this.srcImage.naturalWidth, imgHeight = this.srcImage.naturalHeight, + width = options.width, height = options.height, + maxWidth = options.maxWidth, maxHeight = options.maxHeight, + doSquash = !this.blob || this.blob.type === "image/jpeg"; + if (width && !height) { + height = (imgHeight * width / imgWidth) << 0; + } else if (height && !width) { + width = (imgWidth * height / imgHeight) << 0; + } else { + width = imgWidth; + height = imgHeight; + } + if (maxWidth && width > maxWidth) { + width = maxWidth; + height = (imgHeight * width / imgWidth) << 0; + } + if (maxHeight && height > maxHeight) { + height = maxHeight; + width = (imgWidth * height / imgHeight) << 0; + } + var opt = { width : width, height : height }; + for (var k in options) opt[k] = options[k]; - var tagName = target.tagName.toLowerCase(); - if (tagName === 'img') { - target.src = renderImageToDataURL(this.srcImage, opt, doSquash); - } else if (tagName === 'canvas') { - renderImageToCanvas(this.srcImage, target, opt, doSquash); - } - if (typeof this.onrender === 'function') { - this.onrender(target); - } - }; + var tagName = target.tagName.toLowerCase(); + if (tagName === "img") { + target.src = renderImageToDataURL(this.srcImage, opt, doSquash); + } else if (tagName === "canvas") { + renderImageToCanvas(this.srcImage, target, opt, doSquash); + } + if (typeof this.onrender === "function") { + this.onrender(target); + } + }; - /** - * Export class to global - */ - if (typeof define === 'function' && define.amd) { - define([], function() { return MegaPixImage; }); // for AMD loader - } else { - this.MegaPixImage = MegaPixImage; - } + /** + * Export class to global + */ + if (typeof define === "function" && define.amd) { + define([], function() { return MegaPixImage; }); // for AMD loader + } else { + this.MegaPixImage = MegaPixImage; + } })(); From 25a95255cce486ceb96e0669ab652db58ef9f539 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Mon, 9 Jun 2014 15:05:50 -0500 Subject: [PATCH 006/137] refactor($image-support): move all (owned) strictly image source to a specific folder #1231 --- client/js/{ => image-support}/exif.js | 0 client/js/{ => image-support}/image.js | 0 .../js/{third-party => image-support}/megapix-image.js | 0 client/js/{scaling => image-support}/scaler.js | 0 client/js/{ => image-support}/validation.image.js | 0 lib/modules.js | 10 +++++----- 6 files changed, 5 insertions(+), 5 deletions(-) rename client/js/{ => image-support}/exif.js (100%) rename client/js/{ => image-support}/image.js (100%) rename client/js/{third-party => image-support}/megapix-image.js (100%) rename client/js/{scaling => image-support}/scaler.js (100%) rename client/js/{ => image-support}/validation.image.js (100%) diff --git a/client/js/exif.js b/client/js/image-support/exif.js similarity index 100% rename from client/js/exif.js rename to client/js/image-support/exif.js diff --git a/client/js/image.js b/client/js/image-support/image.js similarity index 100% rename from client/js/image.js rename to client/js/image-support/image.js diff --git a/client/js/third-party/megapix-image.js b/client/js/image-support/megapix-image.js similarity index 100% rename from client/js/third-party/megapix-image.js rename to client/js/image-support/megapix-image.js diff --git a/client/js/scaling/scaler.js b/client/js/image-support/scaler.js similarity index 100% rename from client/js/scaling/scaler.js rename to client/js/image-support/scaler.js diff --git a/client/js/validation.image.js b/client/js/image-support/validation.image.js similarity index 100% rename from client/js/validation.image.js rename to client/js/image-support/validation.image.js diff --git a/lib/modules.js b/lib/modules.js index 46c593db8..b8a3e5e54 100644 --- a/lib/modules.js +++ b/lib/modules.js @@ -154,14 +154,14 @@ var fineUploaderModules = { "client/js/third-party/crypto-js/sha1.js" ], "fuImagePreviewModule": [ - "client/js/third-party/megapix-image.js", - "client/js/image.js", - "client/js/exif.js", + "client/js/image-support/megapix-image.js", + "client/js/image-support/image.js", + "client/js/image-support/exif.js", "client/js/identify.js" ], "fuImageValidationModule": [ "client/js/identify.js", - "client/js/validation.image.js" + "client/js/image-support/validation.image.js" ], "fuPasteModule": [ "client/js/paste.js" @@ -193,7 +193,7 @@ var fineUploaderModules = { "client/js/form-support.js" ], "fuScaling": [ - "client/js/scaling/*", + "client/js/image-support/scaler.js", "client/js/third-party/ExifRestorer.js" ], "fuTotalProgress": [ From 9be9017e3d17be91908f011735b56e47075793da Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Mon, 9 Jun 2014 15:06:29 -0500 Subject: [PATCH 007/137] fix($ios-canvas-limit): flag for canvas size limit #1231 --- client/js/features.js | 1 + docs/browser-support.jmd | 1 + 2 files changed, 2 insertions(+) diff --git a/client/js/features.js b/client/js/features.js index 0e95ea7bf..98758e0d8 100644 --- a/client/js/features.js +++ b/client/js/features.js @@ -132,6 +132,7 @@ qq.supportedFeatures = (function () { imagePreviews: supportsImagePreviews, imageValidation: supportsImagePreviews, itemSizeValidation: supportsAjaxFileUploading, + limitedScaledImageSize: qq.ios(), pause: supportsChunking, progressBar: supportsUploadProgress, resume: supportsResume, diff --git a/docs/browser-support.jmd b/docs/browser-support.jmd index 9f8bc9b52..61303e68a 100644 --- a/docs/browser-support.jmd +++ b/docs/browser-support.jmd @@ -400,6 +400,7 @@ Feature | Description `folderSelection` | Can folders be selected via a file dialog? `imagePreviews` | Is client-side image preview generation possible? `imageValidation` | Can we validate image properties client-side? +`limitedScaledImageSize` | Does the browser have an upper limit on the size of a ``? Directly related to the maximum allowable dimensions of a scaled image. `itemSizeValidation` | Is client-side file size validation supported? `pause` | Can chunked file uploads be paused? `progressBar` | Are progress bars supported? From 726465db7de3d04cb0c6f077ebeb08f8115e64c0 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Mon, 9 Jun 2014 15:12:02 -0500 Subject: [PATCH 008/137] refactor($megapix-image): namespace MegaPixImage library #1231 --- client/js/image-support/image.js | 6 +++--- client/js/image-support/megapix-image.js | 10 +--------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/client/js/image-support/image.js b/client/js/image-support/image.js index 5ab99ccc8..760874de5 100644 --- a/client/js/image-support/image.js +++ b/client/js/image-support/image.js @@ -1,4 +1,4 @@ -/*globals qq, MegaPixImage */ +/*globals qq */ /** * Draws a thumbnail of a Blob/File/URL onto an or . * @@ -156,7 +156,7 @@ qq.ImageGenerator = function(log) { } }, exif = orient ? new qq.Exif(fileOrBlob, log) : dummyExif, - mpImg = new MegaPixImage(fileOrBlob, megapixErrorHandler); + mpImg = new qq.MegaPixImage(fileOrBlob, megapixErrorHandler); if (registerThumbnailRenderedListener(container, drawPreview)) { exif.parse().then( @@ -208,7 +208,7 @@ qq.ImageGenerator = function(log) { tempImgRender.then(function() { registerThumbnailRenderedListener(canvasOrImg, draw); - var mpImg = new MegaPixImage(tempImg); + var mpImg = new qq.MegaPixImage(tempImg); mpImg.render(canvasOrImg, { maxWidth: maxSize, maxHeight: maxSize, diff --git a/client/js/image-support/megapix-image.js b/client/js/image-support/megapix-image.js index 5df9c4d66..d94288169 100644 --- a/client/js/image-support/megapix-image.js +++ b/client/js/image-support/megapix-image.js @@ -263,13 +263,5 @@ } }; - /** - * Export class to global - */ - if (typeof define === "function" && define.amd) { - define([], function() { return MegaPixImage; }); // for AMD loader - } else { - this.MegaPixImage = MegaPixImage; - } - + qq.MegaPixImage = MegaPixImage; })(); From 8d0222ad391638f9245519857a89325ba7a2e6e5 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Wed, 11 Jun 2014 12:06:44 -0500 Subject: [PATCH 009/137] chore($docs): Rename main.js->sidebar.js [skip ci] --- docs/_static/js/sidebar.js | 56 ++++++++++++++++++++++++++++++++++++++ docs/api/events.jmd | 2 +- docs/api/methods-azure.jmd | 2 +- docs/api/methods-s3.jmd | 2 +- docs/api/methods.jmd | 2 +- docs/api/options-azure.jmd | 2 +- docs/api/options-s3.jmd | 2 +- docs/api/options-ui.jmd | 2 +- docs/api/options.jmd | 2 +- 9 files changed, 64 insertions(+), 8 deletions(-) create mode 100755 docs/_static/js/sidebar.js diff --git a/docs/_static/js/sidebar.js b/docs/_static/js/sidebar.js new file mode 100755 index 000000000..815ee4150 --- /dev/null +++ b/docs/_static/js/sidebar.js @@ -0,0 +1,56 @@ +var renderSidebarNav = function(type, headers) { + var $sidebar = $(".sidebar .accordion"), + sidebarContents = ''; + + $.each(headers, function (index, header) { + console.log('header: ' + header); + var $typeEl = $("." + type + "s-" + header), + typeTitle = $typeEl.find('h2').text(), + typeLink = "#"+$typeEl.find('h2').attr('id'); + + sidebarContents += "
" + + "" + + "
" + + "
" + + "" + + "
" + + "
" + + "
"; + }); + $sidebar.append(sidebarContents); +} + +var renderOptionsSidebarNav = function(option_types) { + renderSidebarNav('option', option_types); +} + +var renderMethodsSidebarNav = function(method_types) { + renderSidebarNav('method', method_types); +} + +var renderEventsSidebarNav = function(event_types) { + renderSidebarNav('event', event_types); +} diff --git a/docs/api/events.jmd b/docs/api/events.jmd index 6c42ea06a..4b670f05f 100644 --- a/docs/api/events.jmd +++ b/docs/api/events.jmd @@ -7,7 +7,7 @@ {% endblock %} {% block js_head %} - + {% endblock %} {% block js_footer %} diff --git a/docs/api/methods-azure.jmd b/docs/api/methods-azure.jmd index f9f1812f2..da2cec1b4 100644 --- a/docs/api/methods-azure.jmd +++ b/docs/api/methods-azure.jmd @@ -7,7 +7,7 @@ {% endblock %} {% block js_head %} - + {% endblock %} {% block js_footer %} diff --git a/docs/api/methods-s3.jmd b/docs/api/methods-s3.jmd index f9a5a2389..23591a7c1 100644 --- a/docs/api/methods-s3.jmd +++ b/docs/api/methods-s3.jmd @@ -7,7 +7,7 @@ {% endblock %} {% block js_head %} - + {% endblock %} {% block js_footer %} diff --git a/docs/api/methods.jmd b/docs/api/methods.jmd index 1cd54f280..9d45fe01a 100644 --- a/docs/api/methods.jmd +++ b/docs/api/methods.jmd @@ -9,7 +9,7 @@ {% endblock %} {% block js_head %} - + {% endblock %} {% block js_footer %} diff --git a/docs/api/options-azure.jmd b/docs/api/options-azure.jmd index 43a1e3b70..00955fc29 100644 --- a/docs/api/options-azure.jmd +++ b/docs/api/options-azure.jmd @@ -7,7 +7,7 @@ {% endblock %} {% block js_head %} - + {% endblock %} {% block js_footer %} diff --git a/docs/api/options-s3.jmd b/docs/api/options-s3.jmd index c2ce85de7..84eadfd24 100644 --- a/docs/api/options-s3.jmd +++ b/docs/api/options-s3.jmd @@ -7,7 +7,7 @@ {% endblock %} {% block js_head %} - + {% endblock %} {% block js_footer %} diff --git a/docs/api/options-ui.jmd b/docs/api/options-ui.jmd index 9e6f74756..e0079eed8 100644 --- a/docs/api/options-ui.jmd +++ b/docs/api/options-ui.jmd @@ -7,7 +7,7 @@ {% endblock %} {% block js_head %} - + {% endblock %} {% block js_footer %} diff --git a/docs/api/options.jmd b/docs/api/options.jmd index 54b980888..ca15211e4 100644 --- a/docs/api/options.jmd +++ b/docs/api/options.jmd @@ -7,7 +7,7 @@ {% endblock %} {% block js_head %} - + {% endblock %} {% block js_footer %} From 99d517a7207ba3a6d0d4d0599b1926c118ef607b Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Wed, 11 Jun 2014 12:10:40 -0500 Subject: [PATCH 010/137] fix($docs): Dropdown menu are too big Dropdown-menus that were over the screen height (i.e., Features) were not displaying all elements. This change ensures they are always 80% of the window height, and adds a scrollbar when needed. Fixes #1234 [skip ci] --- docs/_static/js/main.js | 66 ++++++++----------------------------- docs/_templates/layout.html | 2 ++ 2 files changed, 16 insertions(+), 52 deletions(-) diff --git a/docs/_static/js/main.js b/docs/_static/js/main.js index 815ee4150..5e40b9c41 100755 --- a/docs/_static/js/main.js +++ b/docs/_static/js/main.js @@ -1,56 +1,18 @@ -var renderSidebarNav = function(type, headers) { - var $sidebar = $(".sidebar .accordion"), - sidebarContents = ''; +/** + * Resize the dropdown-menu's to be 80% of the window height, and + * add an overflow-y property so that all the elements are shown. + */ +$(function() { + 'use strict'; - $.each(headers, function (index, header) { - console.log('header: ' + header); - var $typeEl = $("." + type + "s-" + header), - typeTitle = $typeEl.find('h2').text(), - typeLink = "#"+$typeEl.find('h2').attr('id'); + var setDropdownHeight = function(){ - sidebarContents += "
" + - "" + - "
" + - "
" + - "" + - "
" + - "
" + - "
"; - }); - $sidebar.append(sidebarContents); -} - -var renderOptionsSidebarNav = function(option_types) { - renderSidebarNav('option', option_types); -} - -var renderMethodsSidebarNav = function(method_types) { - renderSidebarNav('method', method_types); -} - -var renderEventsSidebarNav = function(event_types) { - renderSidebarNav('event', event_types); -} + $(window).resize(setDropdownHeight); + setDropdownHeight(); +}); diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html index 2a2d5f95b..abab04c5f 100755 --- a/docs/_templates/layout.html +++ b/docs/_templates/layout.html @@ -111,6 +111,8 @@ {% endblock %} {% endblock %} + + {% block js_footer %} {# Add your javascript here #} {% endblock %} From 5af335641b01f85da63a6f2d52e0836abd990fad Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Wed, 11 Jun 2014 12:38:52 -0500 Subject: [PATCH 011/137] chore: Remove extra console.log [skip ci] --- docs/_static/js/sidebar.js | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/_static/js/sidebar.js b/docs/_static/js/sidebar.js index 815ee4150..acf87b74d 100755 --- a/docs/_static/js/sidebar.js +++ b/docs/_static/js/sidebar.js @@ -3,7 +3,6 @@ var renderSidebarNav = function(type, headers) { sidebarContents = ''; $.each(headers, function (index, header) { - console.log('header: ' + header); var $typeEl = $("." + type + "s-" + header), typeTitle = $typeEl.find('h2').text(), typeLink = "#"+$typeEl.find('h2').attr('id'); From 2dcc374e5488bc62b8d40f4deca7ba4da0c01178 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Wed, 11 Jun 2014 12:38:52 -0500 Subject: [PATCH 012/137] chore: Remove extra console.log [skip ci] --- docs/_static/js/sidebar.js | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/_static/js/sidebar.js b/docs/_static/js/sidebar.js index 815ee4150..acf87b74d 100755 --- a/docs/_static/js/sidebar.js +++ b/docs/_static/js/sidebar.js @@ -3,7 +3,6 @@ var renderSidebarNav = function(type, headers) { sidebarContents = ''; $.each(headers, function (index, header) { - console.log('header: ' + header); var $typeEl = $("." + type + "s-" + header), typeTitle = $typeEl.find('h2').text(), typeLink = "#"+$typeEl.find('h2').attr('id'); From 815877d3ab8eee4676a9a58e2268a3504a20a0df Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Wed, 11 Jun 2014 12:43:00 -0500 Subject: [PATCH 013/137] chore: Fix 5.0.1 development status --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed7c15c00..0892cefe3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Fine Uploader](http://fineuploader.com/img/FineUploader_logo.png)](http://fineuploader.com/) -Version: 5.0.1 (IN PROGRESS) +Version: 5.0.1 [![Build Status](https://travis-ci.org/Widen/fine-uploader.png?branch=master)](https://travis-ci.org/Widen/fine-uploader) | [![Semver badge](http://calm-shore-6115.herokuapp.com/?label=SemVer&value=2.0.0&color=green)](http://semver.org/spec/v2.0.0.html) From 188b631ad77ced1139ef3d7c5046725fff708175 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Wed, 11 Jun 2014 12:43:00 -0500 Subject: [PATCH 014/137] chore: Fix 5.0.1 development status [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed7c15c00..0892cefe3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Fine Uploader](http://fineuploader.com/img/FineUploader_logo.png)](http://fineuploader.com/) -Version: 5.0.1 (IN PROGRESS) +Version: 5.0.1 [![Build Status](https://travis-ci.org/Widen/fine-uploader.png?branch=master)](https://travis-ci.org/Widen/fine-uploader) | [![Semver badge](http://calm-shore-6115.herokuapp.com/?label=SemVer&value=2.0.0&color=green)](http://semver.org/spec/v2.0.0.html) From 397fe9fdf316b585a9fd28d89488b99d9d3e0050 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Wed, 11 Jun 2014 16:36:16 -0500 Subject: [PATCH 015/137] fix($ios-scaling): iOS ignores a canvas greater than ~5MP fixes #1231 --- client/js/features.js | 2 +- client/js/image-support/megapix-image.js | 32 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/client/js/features.js b/client/js/features.js index 98758e0d8..b109ab6c7 100644 --- a/client/js/features.js +++ b/client/js/features.js @@ -132,12 +132,12 @@ qq.supportedFeatures = (function () { imagePreviews: supportsImagePreviews, imageValidation: supportsImagePreviews, itemSizeValidation: supportsAjaxFileUploading, - limitedScaledImageSize: qq.ios(), pause: supportsChunking, progressBar: supportsUploadProgress, resume: supportsResume, scaling: supportsImagePreviews && supportsUploadingBlobs, tiffPreviews: qq.safari(), // Not the best solution, but simple and probably accurate enough (for now) + unlimitedScaledImageSize: !qq.ios(), // false simply indicates that there is some known limit uploading: supportsUploading, uploadCors: supportsUploadCors, uploadCustomHeaders: supportsAjaxFileUploading, diff --git a/client/js/image-support/megapix-image.js b/client/js/image-support/megapix-image.js index d94288169..79e3857c4 100644 --- a/client/js/image-support/megapix-image.js +++ b/client/js/image-support/megapix-image.js @@ -73,6 +73,21 @@ return canvas.toDataURL(mime, options.quality || 0.8); } + function maybeCalculateDownsampledDimensions(spec) { + var maxPixels = 5241809; //iOS specific value + + if (!qq.ios()) { + throw new qq.Error("Downsampled dimensions can only be reliably calculated for iOS!"); + } + + if (spec.origHeight * spec.origWidth > maxPixels) { + return { + newHeight: Math.round(Math.sqrt(maxPixels * (spec.origHeight / spec.origWidth))), + newWidth: Math.round(Math.sqrt(maxPixels * (spec.origWidth / spec.origHeight))) + } + } + } + /** * Rendering image element (with resizing) into the canvas element */ @@ -81,6 +96,23 @@ var width = options.width, height = options.height; var ctx = canvas.getContext("2d"); ctx.save(); + + if (!qq.supportedFeatures.unlimitedScaledImageSize) { + var modifiedDimensions = maybeCalculateDownsampledDimensions({ + origWidth: width, + origHeight: height + }); + + if (modifiedDimensions) { + qq.log(qq.format("Had to reduce dimensions due to device limitations from {}w / {}h to {}w / {}h", + width, height, modifiedDimensions.newWidth, modifiedDimensions.newHeight), + "warn"); + + width = modifiedDimensions.newWidth - 1; + height = modifiedDimensions.newHeight - 1; + } + } + transformCoordinate(canvas, width, height, options.orientation); // Fine Uploader specific: Save some CPU cycles if not using iOS From deea85a407f3d4af3e2a7dc3e6760274937e0e89 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Wed, 11 Jun 2014 16:40:49 -0500 Subject: [PATCH 016/137] docs($ios-scaling): document iOS limitation [skip ci] #1231 --- docs/browser-support.jmd | 1 + docs/features/scaling.jmd | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/docs/browser-support.jmd b/docs/browser-support.jmd index 61303e68a..0d94553d7 100644 --- a/docs/browser-support.jmd +++ b/docs/browser-support.jmd @@ -407,6 +407,7 @@ Feature | Description `resume` | Is the auto-resume feature supported? `scaling` | Is client-side image scaling possible? `tiffPreviews` | Can TIFFs be rendered natively in the current browser? +`unlimitedScaledImageSize` | `true` if there is no known upper limit for `` (used to scale images). Will be `false` for iOS and scaled images may be further downsampled by Fine Uploader to work around this limit. `uploadCors` | Are cross-domain uploads supported? `uploading` | Is uploading of any sort supported? (Note: This will return true for browsers that are not explicitly supported such as IE6) `uploadCustomHeaders` | Are custom headers allowed to be sent along with the upload request? diff --git a/docs/features/scaling.jmd b/docs/features/scaling.jmd index b11cd5882..6b51aed9b 100644 --- a/docs/features/scaling.jmd +++ b/docs/features/scaling.jmd @@ -171,6 +171,11 @@ this item limit. * Client-side scaling and EXIF header re-insertion operations can potentially be very resource-intensive. Please keep this in mind before you enable this feature, taking into account your user base and application goals. +* iOS limits the size of a `` to about 5 megapixels. Any image larger than this will be rendered as a +blank ``. This affects scaling as Fine Uploader uses `` in part of the scaling process. To +overcome this issue, Fine Uploader will further downsample scaled images that exceed this limit. You can check to +see if such a limitation is known by examining the `qq.supportedFeatures.unlimitedScaledImageSize` flag. + ### Supported Browsers This feature is supported on all browsers other than IE9 and older, Android 2.4 or older, and Safari 5.1 or older. From 67a2502c73638d4b6815b7aa42f1d56771a411e6 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Wed, 11 Jun 2014 16:42:52 -0500 Subject: [PATCH 017/137] chore($ios-scaling): 5.0.2 release candidate [skip ci] #1231 --- README.md | 2 +- bower.json | 2 +- client/js/version.js | 2 +- fineuploader.jquery.json | 2 +- package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ed7c15c00..0980ab8d7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Fine Uploader](http://fineuploader.com/img/FineUploader_logo.png)](http://fineuploader.com/) -Version: 5.0.1 (IN PROGRESS) +Version: 5.0.2 (IN PROGRESS) [![Build Status](https://travis-ci.org/Widen/fine-uploader.png?branch=master)](https://travis-ci.org/Widen/fine-uploader) | [![Semver badge](http://calm-shore-6115.herokuapp.com/?label=SemVer&value=2.0.0&color=green)](http://semver.org/spec/v2.0.0.html) diff --git a/bower.json b/bower.json index ba91bf189..9968fb821 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "fine-uploader", - "version": "5.0.1", + "version": "5.0.2", "devDependencies": { "jquery": "1.10.0", "purl": "https://github.com/allmarkedup/purl.git#~2.3.1", diff --git a/client/js/version.js b/client/js/version.js index 89afb6f31..67764d5f9 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version="5.0.1"; +qq.version="5.0.2"; diff --git a/fineuploader.jquery.json b/fineuploader.jquery.json index ca25acbb7..a68fb9d29 100644 --- a/fineuploader.jquery.json +++ b/fineuploader.jquery.json @@ -16,7 +16,7 @@ "azure", "microsoft" ], - "version": "5.0.1", + "version": "5.0.2", "author": { "name": "Widen Enterprises", "url": "http://widen.com" diff --git a/package.json b/package.json index 6b1bea7e5..0c8d784e3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fineuploader", "title": "Fine Uploader", - "version": "5.0.1", + "version": "5.0.2", "description": "Multiple file upload component with progress-bar, drag-and-drop, support for all modern browsers.", "main": "./fine-uploader/js/fineuploader.js", "maintainers": [ From d2e8dc0357434ff094e8a9f36eedd44f5a0169b8 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Wed, 11 Jun 2014 16:58:46 -0500 Subject: [PATCH 018/137] fix($ios-scaling): re-adjust max pixel limit for iOS #1231 --- client/js/image-support/megapix-image.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/js/image-support/megapix-image.js b/client/js/image-support/megapix-image.js index 79e3857c4..118d5df31 100644 --- a/client/js/image-support/megapix-image.js +++ b/client/js/image-support/megapix-image.js @@ -74,7 +74,7 @@ } function maybeCalculateDownsampledDimensions(spec) { - var maxPixels = 5241809; //iOS specific value + var maxPixels = 5241000; //iOS specific value if (!qq.ios()) { throw new qq.Error("Downsampled dimensions can only be reliably calculated for iOS!"); @@ -108,8 +108,8 @@ width, height, modifiedDimensions.newWidth, modifiedDimensions.newHeight), "warn"); - width = modifiedDimensions.newWidth - 1; - height = modifiedDimensions.newHeight - 1; + width = modifiedDimensions.newWidth; + height = modifiedDimensions.newHeight; } } From 88d7aca3c6faa7c7ee71b84c31e7e0ef6a8df769 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 12 Jun 2014 11:33:17 -0500 Subject: [PATCH 019/137] chore($build): inc build num - prerelease [skip ci] #1231 --- README.md | 2 +- bower.json | 2 +- client/js/version.js | 2 +- fineuploader.jquery.json | 2 +- package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0980ab8d7..fb6dd86bc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Fine Uploader](http://fineuploader.com/img/FineUploader_logo.png)](http://fineuploader.com/) -Version: 5.0.2 (IN PROGRESS) +Version: 5.0.2-1 (IN PROGRESS) [![Build Status](https://travis-ci.org/Widen/fine-uploader.png?branch=master)](https://travis-ci.org/Widen/fine-uploader) | [![Semver badge](http://calm-shore-6115.herokuapp.com/?label=SemVer&value=2.0.0&color=green)](http://semver.org/spec/v2.0.0.html) diff --git a/bower.json b/bower.json index 9968fb821..7258458ad 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "fine-uploader", - "version": "5.0.2", + "version": "5.0.2-1", "devDependencies": { "jquery": "1.10.0", "purl": "https://github.com/allmarkedup/purl.git#~2.3.1", diff --git a/client/js/version.js b/client/js/version.js index 67764d5f9..da3ccc7a7 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version="5.0.2"; +qq.version="5.0.2-1"; diff --git a/fineuploader.jquery.json b/fineuploader.jquery.json index a68fb9d29..f3014b7b6 100644 --- a/fineuploader.jquery.json +++ b/fineuploader.jquery.json @@ -16,7 +16,7 @@ "azure", "microsoft" ], - "version": "5.0.2", + "version": "5.0.2-1", "author": { "name": "Widen Enterprises", "url": "http://widen.com" diff --git a/package.json b/package.json index 0c8d784e3..0483cc5b5 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fineuploader", "title": "Fine Uploader", - "version": "5.0.2", + "version": "5.0.2-1", "description": "Multiple file upload component with progress-bar, drag-and-drop, support for all modern browsers.", "main": "./fine-uploader/js/fineuploader.js", "maintainers": [ From 8dbadba495c0b7f9746e073ef623459ded874c75 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 13 Jun 2014 11:41:09 -0500 Subject: [PATCH 020/137] =?UTF-8?q?chore:=20=E2=9C=A8=F0=9F=8E=86=20Belate?= =?UTF-8?q?d=20Happy=20New=20Year!=F0=9F=8E=86=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/_templates/footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_templates/footer.html b/docs/_templates/footer.html index 206c35d41..68547a90c 100644 --- a/docs/_templates/footer.html +++ b/docs/_templates/footer.html @@ -1,4 +1,4 @@ From f7c237774904339fb60eddbbd96bdae2705f9371 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 13 Jun 2014 11:41:35 -0500 Subject: [PATCH 021/137] docs($browser-support): Update multiple file selection browser support matrix column [skip ci] --- docs/browser-support.jmd | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/browser-support.jmd b/docs/browser-support.jmd index 9f8bc9b52..5f385af25 100644 --- a/docs/browser-support.jmd +++ b/docs/browser-support.jmd @@ -31,6 +31,7 @@ Note: Any features not listed here are supported in all browsers. Browser XHR2 + Multiple File Selection File Drop Folder Drop Folder Selection @@ -67,10 +68,12 @@ Note: Any features not listed here are supported in all browsers. {{ label('√', "success") }} {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} Chrome for iOS 6+ {{ label('√', "success") }} + {{ label('√', "success") }}* {{ label('x', "") }} {{ label('x', "") }} {{ label('x', "") }} @@ -90,6 +93,7 @@ Note: Any features not listed here are supported in all browsers. Chrome for Android 4+ {{ label('√', "success") }} + {{ label('x', "important") }} {{ label('x', "") }} {{ label('x', "") }} {{ label('x', "") }} @@ -124,11 +128,13 @@ Note: Any features not listed here are supported in all browsers. {{ label('√', "success") }} {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} Firefox {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} {{ label('√', "success") }} @@ -148,6 +154,7 @@ Note: Any features not listed here are supported in all browsers. IE 11 {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} {{ label('√', "success") }} @@ -167,6 +174,7 @@ Note: Any features not listed here are supported in all browsers. IE 10 {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} {{ label('√', "success") }} @@ -191,6 +199,7 @@ Note: Any features not listed here are supported in all browsers. {{ label('x', "important") }} {{ label('x', "important") }} {{ label('x', "important") }} + {{ label('x', "important") }} {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} @@ -210,6 +219,7 @@ Note: Any features not listed here are supported in all browsers. {{ label('x', "important") }} {{ label('x', "important") }} {{ label('x', "important") }} + {{ label('x', "important") }} {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} @@ -236,6 +246,7 @@ Note: Any features not listed here are supported in all browsers. {{ label('x', "important") }} {{ label('x', "important") }} {{ label('x', "important") }} + {{ label('x', "important") }} {{ label('√', "success") }} {{ label('x', "important") }} @@ -243,7 +254,8 @@ Note: Any features not listed here are supported in all browsers. Android 4 stock {{ label('√', "success") }} - {{ label('x', "") }} + {{ label('x', "important") }} + {{ label('x', "important") }} {{ label('x', "") }} {{ label('x', "") }} {{ label('x', "important") }} @@ -263,6 +275,7 @@ Note: Any features not listed here are supported in all browsers. Android 2.3.x stock {{ label('x', "important") }} + {{ label('x', "important") }} {{ label('x', "") }} {{ label('x', "") }} {{ label('x', "") }} @@ -283,6 +296,7 @@ Note: Any features not listed here are supported in all browsers. iOS 6+ {{ label('√', "success") }} + {{ label('√', "success") }}* {{ label('x', "") }} {{ label('x', "") }} {{ label('x', "") }} @@ -304,6 +318,7 @@ Note: Any features not listed here are supported in all browsers. 6 {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} {{ label('√', "success") }} @@ -324,6 +339,7 @@ Note: Any features not listed here are supported in all browsers. 5.1 {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} {{ label('x', "important") }} @@ -342,6 +358,9 @@ Note: Any features not listed here are supported in all browsers. +* - iOS browsers are unable to upload multiple files when video files are allowed to be uploaded due to a long-standing iOS bug. See case #990 on our bug tracker for more details. + + ### Upload size Limitations Upload size limitations are present in all browsers, but are most likely to affect the users of older browsers From b3c50779c8c23ba1ee1d6f695b86b8d71e7941ec Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 13 Jun 2014 11:41:09 -0500 Subject: [PATCH 022/137] =?UTF-8?q?chore:=20=E2=9C=A8=F0=9F=8E=86=20Belate?= =?UTF-8?q?d=20Happy=20New=20Year!=F0=9F=8E=86=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [skip ci] --- docs/_templates/footer.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_templates/footer.html b/docs/_templates/footer.html index 206c35d41..68547a90c 100644 --- a/docs/_templates/footer.html +++ b/docs/_templates/footer.html @@ -1,4 +1,4 @@ From 4955fd30574425e3fb44697adf08dd64f7abe819 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 13 Jun 2014 11:41:35 -0500 Subject: [PATCH 023/137] docs($browser-support): Update multiple file selection browser support matrix column [skip ci] --- docs/browser-support.jmd | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/browser-support.jmd b/docs/browser-support.jmd index 9f8bc9b52..5f385af25 100644 --- a/docs/browser-support.jmd +++ b/docs/browser-support.jmd @@ -31,6 +31,7 @@ Note: Any features not listed here are supported in all browsers. Browser XHR2 + Multiple File Selection File Drop Folder Drop Folder Selection @@ -67,10 +68,12 @@ Note: Any features not listed here are supported in all browsers. {{ label('√', "success") }} {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} Chrome for iOS 6+ {{ label('√', "success") }} + {{ label('√', "success") }}* {{ label('x', "") }} {{ label('x', "") }} {{ label('x', "") }} @@ -90,6 +93,7 @@ Note: Any features not listed here are supported in all browsers. Chrome for Android 4+ {{ label('√', "success") }} + {{ label('x', "important") }} {{ label('x', "") }} {{ label('x', "") }} {{ label('x', "") }} @@ -124,11 +128,13 @@ Note: Any features not listed here are supported in all browsers. {{ label('√', "success") }} {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} Firefox {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} {{ label('√', "success") }} @@ -148,6 +154,7 @@ Note: Any features not listed here are supported in all browsers. IE 11 {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} {{ label('√', "success") }} @@ -167,6 +174,7 @@ Note: Any features not listed here are supported in all browsers. IE 10 {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} {{ label('√', "success") }} @@ -191,6 +199,7 @@ Note: Any features not listed here are supported in all browsers. {{ label('x', "important") }} {{ label('x', "important") }} {{ label('x', "important") }} + {{ label('x', "important") }} {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} @@ -210,6 +219,7 @@ Note: Any features not listed here are supported in all browsers. {{ label('x', "important") }} {{ label('x', "important") }} {{ label('x', "important") }} + {{ label('x', "important") }} {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} @@ -236,6 +246,7 @@ Note: Any features not listed here are supported in all browsers. {{ label('x', "important") }} {{ label('x', "important") }} {{ label('x', "important") }} + {{ label('x', "important") }} {{ label('√', "success") }} {{ label('x', "important") }} @@ -243,7 +254,8 @@ Note: Any features not listed here are supported in all browsers. Android 4 stock {{ label('√', "success") }} - {{ label('x', "") }} + {{ label('x', "important") }} + {{ label('x', "important") }} {{ label('x', "") }} {{ label('x', "") }} {{ label('x', "important") }} @@ -263,6 +275,7 @@ Note: Any features not listed here are supported in all browsers. Android 2.3.x stock {{ label('x', "important") }} + {{ label('x', "important") }} {{ label('x', "") }} {{ label('x', "") }} {{ label('x', "") }} @@ -283,6 +296,7 @@ Note: Any features not listed here are supported in all browsers. iOS 6+ {{ label('√', "success") }} + {{ label('√', "success") }}* {{ label('x', "") }} {{ label('x', "") }} {{ label('x', "") }} @@ -304,6 +318,7 @@ Note: Any features not listed here are supported in all browsers. 6 {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} {{ label('√', "success") }} @@ -324,6 +339,7 @@ Note: Any features not listed here are supported in all browsers. 5.1 {{ label('√', "success") }} {{ label('√', "success") }} + {{ label('√', "success") }} {{ label('x', "important") }} {{ label('x', "important") }} {{ label('x', "important") }} @@ -342,6 +358,9 @@ Note: Any features not listed here are supported in all browsers. +* - iOS browsers are unable to upload multiple files when video files are allowed to be uploaded due to a long-standing iOS bug. See case #990 on our bug tracker for more details. + + ### Upload size Limitations Upload size limitations are present in all browsers, but are most likely to affect the users of older browsers From 831031fdee75c1d65e004388f2fb8e21431fb375 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 17 Jun 2014 13:21:18 -0500 Subject: [PATCH 024/137] chore($release): prepare for 5.0.2 release [skip ci] #1231 --- README.md | 2 +- bower.json | 2 +- client/js/version.js | 2 +- fineuploader.jquery.json | 2 +- package.json | 2 +- test/raytest-jquery.html | 53 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 58 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fb6dd86bc..0980ab8d7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Fine Uploader](http://fineuploader.com/img/FineUploader_logo.png)](http://fineuploader.com/) -Version: 5.0.2-1 (IN PROGRESS) +Version: 5.0.2 (IN PROGRESS) [![Build Status](https://travis-ci.org/Widen/fine-uploader.png?branch=master)](https://travis-ci.org/Widen/fine-uploader) | [![Semver badge](http://calm-shore-6115.herokuapp.com/?label=SemVer&value=2.0.0&color=green)](http://semver.org/spec/v2.0.0.html) diff --git a/bower.json b/bower.json index 7258458ad..9968fb821 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "fine-uploader", - "version": "5.0.2-1", + "version": "5.0.2", "devDependencies": { "jquery": "1.10.0", "purl": "https://github.com/allmarkedup/purl.git#~2.3.1", diff --git a/client/js/version.js b/client/js/version.js index da3ccc7a7..67764d5f9 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version="5.0.2-1"; +qq.version="5.0.2"; diff --git a/fineuploader.jquery.json b/fineuploader.jquery.json index f3014b7b6..a68fb9d29 100644 --- a/fineuploader.jquery.json +++ b/fineuploader.jquery.json @@ -16,7 +16,7 @@ "azure", "microsoft" ], - "version": "5.0.2-1", + "version": "5.0.2", "author": { "name": "Widen Enterprises", "url": "http://widen.com" diff --git a/package.json b/package.json index 0483cc5b5..0c8d784e3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fineuploader", "title": "Fine Uploader", - "version": "5.0.2-1", + "version": "5.0.2", "description": "Multiple file upload component with progress-bar, drag-and-drop, support for all modern browsers.", "main": "./fine-uploader/js/fineuploader.js", "maintainers": [ diff --git a/test/raytest-jquery.html b/test/raytest-jquery.html index cfceab822..2beb98b50 100644 --- a/test/raytest-jquery.html +++ b/test/raytest-jquery.html @@ -85,6 +85,59 @@

FineUploaderBasic

Select Files
+ + + +
+ + + + + + + + + + +
Upload
+
+
Full filesize onSubmit:
+
+
Bytes uploaded:
+
+ +
From bf1c8d8d0103a1f2e97db8b79cea839cfd89c3dd Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 17 Jun 2014 13:22:50 -0500 Subject: [PATCH 025/137] test(raytest-jquery.html): remove accidentally checked-in code [skip ci] #1231 --- test/raytest-jquery.html | 53 ---------------------------------------- 1 file changed, 53 deletions(-) diff --git a/test/raytest-jquery.html b/test/raytest-jquery.html index 2beb98b50..cfceab822 100644 --- a/test/raytest-jquery.html +++ b/test/raytest-jquery.html @@ -85,59 +85,6 @@

FineUploaderBasic

Select Files
- - - -
- - - - - - - - - - -
Upload
-
-
Full filesize onSubmit:
-
-
Bytes uploaded:
-
- -
From e73691d10416ba6b8e6158b49928b6dbb956fbaf Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 26 Jun 2014 14:59:05 -0500 Subject: [PATCH 026/137] refactor($dead-code): remove dead code #1238 --- client/js/image-support/scaler.js | 50 +------------------------------ 1 file changed, 1 insertion(+), 49 deletions(-) diff --git a/client/js/image-support/scaler.js b/client/js/image-support/scaler.js index 481e97b34..c6e69bbce 100644 --- a/client/js/image-support/scaler.js +++ b/client/js/image-support/scaler.js @@ -18,55 +18,7 @@ qq.Scaler = function(spec, log) { defaultQuality = spec.defaultQuality / 100, failedToScaleText = spec.failureText, includeExif = spec.includeExif, - sizes = this._getSortedSizes(spec.sizes), - - getFileRecords = function(originalFileUuid, originalFileName, originalBlobOrBlobData) { - var self = this, - records = [], - originalBlob = originalBlobOrBlobData.blob ? originalBlobOrBlobData.blob : originalBlobOrBlobData, - idenitifier = new qq.Identify(originalBlob, log); - - // If the reference file cannot be rendered natively, we can't create scaled versions. - if (idenitifier.isPreviewableSync()) { - // Create records for each scaled version & add them to the records array, smallest first. - qq.each(sizes, function(idx, sizeRecord) { - var outputType = self._determineOutputType({ - defaultType: defaultType, - requestedType: sizeRecord.type, - refType: originalBlob.type - }); - - records.push({ - uuid: qq.getUniqueId(), - name: self._getName(originalFileName, { - name: sizeRecord.name, - type: outputType, - refType: originalBlob.type - }), - blob: new qq.BlobProxy(originalBlob, - qq.bind(self._generateScaledImage, self, { - maxSize: sizeRecord.maxSize, - orient: orient, - type: outputType, - quality: defaultQuality, - failedText: failedToScaleText, - includeExif: includeExif, - log: log - })) - } - ); - }); - } - - // Finally, add a record for the original file (if requested) - includeReference && records.push({ - uuid: originalFileUuid, - name: originalFileName, - blob: originalBlob - }); - - return records; - }; + sizes = this._getSortedSizes(spec.sizes); // Revealed API for instances of this module qq.extend(this, { From 053cfc3f397268b3b916f7f8f3c5b25dc9fbfaf2 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 26 Jun 2014 15:26:41 -0500 Subject: [PATCH 027/137] fix(identify.js): normalized blob name not always being logged #1238 --- client/js/identify.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/js/identify.js b/client/js/identify.js index e0081da9d..99c8f95d1 100644 --- a/client/js/identify.js +++ b/client/js/identify.js @@ -59,7 +59,7 @@ qq.Identify = function(fileOrBlob, log) { } }, function() { - log("Error reading file w/ name '" + fileOrBlob.name + "'. Not able to be rendered in this browser."); + log("Error reading file w/ name '" + name + "'. Not able to be rendered in this browser."); idenitifer.failure(); }); } @@ -82,7 +82,8 @@ qq.Identify = function(fileOrBlob, log) { var fileMime = fileOrBlob.type, // Assumption: This will only ever be executed in browsers that support `Object.keys`. isRecognizedImage = qq.indexOf(Object.keys(this.PREVIEWABLE_MIME_TYPES), fileMime) >= 0, - previewable = false; + previewable = false, + name = fileOrBlob.name === undefined ? "blob" : fileOrBlob.name; if (isRecognizedImage) { if (fileMime === "image/tiff") { @@ -93,7 +94,7 @@ qq.Identify = function(fileOrBlob, log) { } } - !previewable && log(fileOrBlob.name + " is not previewable in this browser per the blob's type attr"); + !previewable && log(name + " is not previewable in this browser per the blob's type attr"); return previewable; } From 2a4ddd7879e0db78ca1559afcc8ef074e33012ca Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Thu, 26 Jun 2014 15:28:37 -0500 Subject: [PATCH 028/137] fix($sendOriginal): non-image ignored w/ scaling.sendOriginal: false In addition to ensuring that non-images are not ignored when scaling is turned on and the sendOriginal option is false, I also needed to adjust other logic that was unintentionally dependant on this faulty logic. fixes #1238 --- client/js/image-support/scaler.js | 26 +++++++++++--------- test/unit/scaling.js | 40 +++++++++++++++++++++++++++---- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/client/js/image-support/scaler.js b/client/js/image-support/scaler.js index c6e69bbce..cef03904e 100644 --- a/client/js/image-support/scaler.js +++ b/client/js/image-support/scaler.js @@ -62,14 +62,20 @@ qq.Scaler = function(spec, log) { } ); }); - } - // Finally, add a record for the original file (if requested) - includeReference && records.push({ - uuid: originalFileUuid, - name: originalFileName, - blob: originalBlob - }); + includeReference && records.push({ + uuid: originalFileUuid, + name: originalFileName, + blob: originalBlob + }); + } + else { + records.push({ + uuid: originalFileUuid, + name: originalFileName, + blob: originalBlob + }); + } return records; }, @@ -179,12 +185,10 @@ qq.extend(qq.Scaler.prototype, { } else { (qq.bind(function() { - var record; - // Assumption: There will never be more than one record - record = scaler.getFileRecords(uuid, name, file)[0]; + var record = scaler.getFileRecords(uuid, name, file)[0]; - if (record) { + if (record && record.blob instanceof qq.BlobProxy) { record.blob.create().then(scalingEffort.success, scalingEffort.failure); } else { diff --git a/test/unit/scaling.js b/test/unit/scaling.js index ffb4a9c0d..7eeafc1df 100644 --- a/test/unit/scaling.js +++ b/test/unit/scaling.js @@ -29,7 +29,7 @@ if (qq.supportedFeatures.scaling) { }); describe("generate records tests", function() { - function runTest(includeOriginal) { + function runTestWithImage(includeOriginal) { var sizes = [ { name: "small", @@ -47,7 +47,7 @@ if (qq.supportedFeatures.scaling) { type: "image/jpeg" } ], - originalFile = {dummy: "blob", type:"image/jpeg"}, + originalFile = {dummy: "blob", type: "image/jpeg"}, scaler = new qq.Scaler(({sizes: sizes, sendOriginal: includeOriginal})), records = scaler.getFileRecords("originalUuid", "originalName.jpeg", originalFile); @@ -72,12 +72,44 @@ if (qq.supportedFeatures.scaling) { } } + function runTestWithNonImage(includeOriginal) { + var sizes = [ + { + name: "small", + maxSize: 100, + type: "image/jpeg" + }, + { + name: "large", + maxSize: 300, + type: "image/jpeg" + } + ], + originalFile = {dummy: "blob", type: "text/plain"}, + scaler = new qq.Scaler(({sizes: sizes, sendOriginal: includeOriginal}), function dummyLogger(){}), + records = scaler.getFileRecords("originalUuid", "plain.txt", originalFile); + + assert.equal(records.length, 1); + + assert.equal(records[0].name, "plain.txt"); + assert.equal(records[0].uuid, "originalUuid"); + assert.equal(records[0].blob, originalFile); + } + it("creates properly ordered and constructed file records on demand", function() { - runTest(true); + runTestWithImage(true); }); it("creates properly ordered and constructed file records on demand (ignoring original)", function() { - runTest(false); + runTestWithImage(false); + }); + + it("ensure non-images are not ignored", function() { + runTestWithNonImage(true); + }); + + it("ensure non-images are not ignored, even if `sendOriginal` is set to false", function() { + runTestWithNonImage(false); }); }); From 0cb3c5c047dce146bc7a1daa7e09036a11dd1132 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Mon, 30 Jun 2014 09:19:25 -0500 Subject: [PATCH 029/137] docs($events): Fix Typo in validate callbacks Closes #1240 [skip ci] --- docs/api/events.jmd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/events.jmd b/docs/api/events.jmd index 4b670f05f..20910ec7c 100644 --- a/docs/api/events.jmd +++ b/docs/api/events.jmd @@ -558,8 +558,8 @@ The `blobData` object has two properties: `name` and `size`. The `size` property [ { "name": "data", - "type": "File or Blob", - "description": " A `File` or `Blob`." + "type": "Object", + "description": "An object with a `name` and `size` property." }, { "name": "buttonContainer", @@ -591,7 +591,7 @@ The `fileOrBlobDataArray` object has two properties: `name` and `size`. The `siz { "name": "fileOrBlobDataArray", "type": "Array", - "description": "An array of `File`'s or `Blob`'s." + "description": "An array of `Objects` with `name` and a `size` properties." }, { "name": "buttonContainer", From 5dd065dc705057850948d2144312b4fa06958822 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Mon, 30 Jun 2014 09:19:25 -0500 Subject: [PATCH 030/137] docs($events): Fix Typo in validate callbacks Closes #1240 [skip ci] --- docs/api/events.jmd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api/events.jmd b/docs/api/events.jmd index 4b670f05f..20910ec7c 100644 --- a/docs/api/events.jmd +++ b/docs/api/events.jmd @@ -558,8 +558,8 @@ The `blobData` object has two properties: `name` and `size`. The `size` property [ { "name": "data", - "type": "File or Blob", - "description": " A `File` or `Blob`." + "type": "Object", + "description": "An object with a `name` and `size` property." }, { "name": "buttonContainer", @@ -591,7 +591,7 @@ The `fileOrBlobDataArray` object has two properties: `name` and `size`. The `siz { "name": "fileOrBlobDataArray", "type": "Array", - "description": "An array of `File`'s or `Blob`'s." + "description": "An array of `Objects` with `name` and a `size` properties." }, { "name": "buttonContainer", From 073adf8733838a34346653c1a644a9c0556f477c Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Mon, 30 Jun 2014 09:38:56 -0500 Subject: [PATCH 031/137] docs($events): Extra word typo [skip ci] --- docs/api/events.jmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/events.jmd b/docs/api/events.jmd index 20910ec7c..602334b9b 100644 --- a/docs/api/events.jmd +++ b/docs/api/events.jmd @@ -591,7 +591,7 @@ The `fileOrBlobDataArray` object has two properties: `name` and `size`. The `siz { "name": "fileOrBlobDataArray", "type": "Array", - "description": "An array of `Objects` with `name` and a `size` properties." + "description": "An array of `Objects` with `name` and `size` properties." }, { "name": "buttonContainer", From cb4a50228f64f3f3a7e472d845392c7ac0300f9d Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Mon, 30 Jun 2014 09:38:56 -0500 Subject: [PATCH 032/137] docs($events): Extra word typo [skip ci] --- docs/api/events.jmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/events.jmd b/docs/api/events.jmd index 20910ec7c..602334b9b 100644 --- a/docs/api/events.jmd +++ b/docs/api/events.jmd @@ -591,7 +591,7 @@ The `fileOrBlobDataArray` object has two properties: `name` and `size`. The `siz { "name": "fileOrBlobDataArray", "type": "Array", - "description": "An array of `Objects` with `name` and a `size` properties." + "description": "An array of `Objects` with `name` and `size` properties." }, { "name": "buttonContainer", From a5eec2047b3399e4082a82748ed756c651f740e3 Mon Sep 17 00:00:00 2001 From: Ray Nicholus Date: Tue, 1 Jul 2014 09:28:05 -0500 Subject: [PATCH 033/137] chore($release): prepare for 5.0.3 release [skip ci] #1238 --- README.md | 2 +- bower.json | 2 +- client/js/version.js | 2 +- fineuploader.jquery.json | 2 +- package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4373d1357..750cc2704 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Fine Uploader](http://fineuploader.com/img/FineUploader_logo.png)](http://fineuploader.com/) -Version: 5.0.2 +Version: 5.0.3 [![Build Status](https://travis-ci.org/Widen/fine-uploader.png?branch=master)](https://travis-ci.org/Widen/fine-uploader) | [![Semver badge](http://calm-shore-6115.herokuapp.com/?label=SemVer&value=2.0.0&color=green)](http://semver.org/spec/v2.0.0.html) diff --git a/bower.json b/bower.json index 9968fb821..c53785838 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "fine-uploader", - "version": "5.0.2", + "version": "5.0.3", "devDependencies": { "jquery": "1.10.0", "purl": "https://github.com/allmarkedup/purl.git#~2.3.1", diff --git a/client/js/version.js b/client/js/version.js index 67764d5f9..f548a0e04 100644 --- a/client/js/version.js +++ b/client/js/version.js @@ -1,2 +1,2 @@ /*global qq */ -qq.version="5.0.2"; +qq.version="5.0.3"; diff --git a/fineuploader.jquery.json b/fineuploader.jquery.json index a68fb9d29..8e10159a8 100644 --- a/fineuploader.jquery.json +++ b/fineuploader.jquery.json @@ -16,7 +16,7 @@ "azure", "microsoft" ], - "version": "5.0.2", + "version": "5.0.3", "author": { "name": "Widen Enterprises", "url": "http://widen.com" diff --git a/package.json b/package.json index 0c8d784e3..2e062ca3c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "fineuploader", "title": "Fine Uploader", - "version": "5.0.2", + "version": "5.0.3", "description": "Multiple file upload component with progress-bar, drag-and-drop, support for all modern browsers.", "main": "./fine-uploader/js/fineuploader.js", "maintainers": [ From 8269b8ce22b41745ef0068bc9435f38ccfa4aae3 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 18:53:57 -0500 Subject: [PATCH 034/137] chore(/tasks): #1112 - Move 'clean' task to own module Also using Javascript instead of coffeescript syntax. --- Gruntfile.coffee | 19 +++---------------- lib/grunt/tasks/clean.js | 36 ++++++++++++++++++++++++++++++++++++ lib/grunt/tasks/index.js | 5 +++++ 3 files changed, 44 insertions(+), 16 deletions(-) create mode 100644 lib/grunt/tasks/clean.js create mode 100644 lib/grunt/tasks/index.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index cfbbe3098..e60feaf18 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -6,6 +6,8 @@ ### +tasks = require('./lib/grunt/tasks') + module.exports = (grunt) -> fs = require 'fs' @@ -62,22 +64,7 @@ module.exports = (grunt) -> cleanBowerDir: true layout: 'byComponent' - clean: - build: - files: - src: paths.build - dist: - files: - src: paths.dist - test: - files: - src: ["#{paths.test}/_temp*"] - vendor: - files: - src: "#{paths.test}/_vendor" - custom: - files: - src: "#{paths.custom}/*" + clean: tasks.clean(paths) coffeelint: options: diff --git a/lib/grunt/tasks/clean.js b/lib/grunt/tasks/clean.js new file mode 100644 index 000000000..6b4077bbf --- /dev/null +++ b/lib/grunt/tasks/clean.js @@ -0,0 +1,36 @@ +/* jshint node:true */ +/* globals module */ +var path = require("path"); + +module.exports = function(paths) { + "use strict"; + return { + build: { + // ./{build} + files: { src: paths.build } + }, + + dist: { + // ./{dist} + files: { src: paths.dist } + }, + + test: { + // ./{test}/{vendor} + files: { src: path.join(paths.test, "_temp*") } + + }, + + vendor: { + // ./{test}/_vendor + files: { src: path.join(paths.test, "_vendor") } + + }, + + custom: { + // ./{custom} + files: { src: paths.custom } + + } + }; +}; diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js new file mode 100644 index 000000000..23d8c8cf0 --- /dev/null +++ b/lib/grunt/tasks/index.js @@ -0,0 +1,5 @@ +/* jshint node: true */ +module.exports = module = { + clean: require("./clean"), +}; + From 0cbd128d545f9c43cdced172430ae17e44a9cf6a Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 18:57:40 -0500 Subject: [PATCH 035/137] chore(Gruntfile,package.json): #1112 - Remove grunt-coffeelint and -concurrent Originally I just wanted to remove coffeelint, but one concurrent task depended on it and no one seems to use concurrent anyways. So I removed both. --- Gruntfile.coffee | 25 ------------------------- package.json | 2 -- 2 files changed, 27 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index e60feaf18..a84d9c33e 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -66,18 +66,6 @@ module.exports = (grunt) -> clean: tasks.clean(paths) - coffeelint: - options: - indentation: - level: 'warn' - no_trailing_whitespace: - level: 'warn' - no_backticks: - level: 'ignore' - max_line_length: - level: 'ignore' - grunt: ['./Gruntfile.coffee', 'lib/grunt/**/*.coffee'] - compress: jquery: options: @@ -176,13 +164,6 @@ module.exports = (grunt) -> src: ["#{paths.src}/*.css"] dest: "#{paths.build}/<%= pkg.name %>.css" - concurrent: - minify: ['cssmin', 'uglify'] - lint: ['jshint', 'coffeelint'] - concat: ['concat'] - clean: ['clean'] - compress: ['compress'] - connect: root_server: options: @@ -707,12 +688,6 @@ module.exports = (grunt) -> 'jshint:tests' 'tests:local' ] - grunt: - files: ['./Gruntfile.coffee'] - tasks: [ - 'coffeelint:grunt' - 'build' - ] images: files: ["#{paths.src}/*.gif", "#{paths.src}/placeholders/*.png"] tasks: [ diff --git a/package.json b/package.json index 2e062ca3c..f89ed62c6 100644 --- a/package.json +++ b/package.json @@ -45,8 +45,6 @@ "async": "~0.2.9", "grunt-banner": "~0.2.0", "grunt-bower-task": "0.3.2", - "grunt-coffeelint": "0.0.7", - "grunt-concurrent": "~0.3.1", "grunt-contrib-clean": "~0.4.1", "grunt-contrib-compress": "~0.5.2", "grunt-contrib-concat": "~0.3.0", From f0a48226c7136afdbfa1921660be6599861d2449 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 19:23:35 -0500 Subject: [PATCH 036/137] chore($grunt/tasks): #1112 - Move 'compress' task to own module --- Gruntfile.coffee | 73 +---------------------------- lib/grunt/tasks/compress.js | 91 +++++++++++++++++++++++++++++++++++++ lib/grunt/tasks/index.js | 1 + 3 files changed, 93 insertions(+), 72 deletions(-) create mode 100644 lib/grunt/tasks/compress.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index a84d9c33e..198347fd6 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -65,78 +65,7 @@ module.exports = (grunt) -> layout: 'byComponent' clean: tasks.clean(paths) - - compress: - jquery: - options: - archive: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>.zip" - files: [ - { - expand: true - cwd: paths.dist - src: './jquery.<%= pkg.name %>-<%= pkg.version %>/*' - } - ] - jqueryS3: - options: - archive: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>.zip" - files: [ - { - expand: true - cwd: paths.dist - src: './s3.jquery.<%= pkg.name %>-<%= pkg.version %>/*' - } - ] - jqueryAzure: - options: - archive: "#{paths.dist}/azure.jquery.<%= pkg.name %>-<%= pkg.version %>.zip" - files: [ - { - expand: true - cwd: paths.dist - src: './azure.jquery.<%= pkg.name %>-<%= pkg.version %>/*' - } - ] - core: - options: - archive: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>.zip" - files: [ - { - expand: true - cwd: paths.dist - src: './<%= pkg.name %>-<%= pkg.version %>/*' - } - ] - coreS3: - options: - archive: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>.zip" - files: [ - { - expand: true - cwd: paths.dist - src: './s3.<%= pkg.name %>-<%= pkg.version %>/*' - } - ] - coreAzure: - options: - archive: "#{paths.dist}/azure.<%= pkg.name %>-<%= pkg.version %>.zip" - files: [ - { - expand: true - cwd: paths.dist - src: './azure.<%= pkg.name %>-<%= pkg.version %>/*' - } - ] - custom: - options: - archive: "#{customBuildDest}/custom.<%= pkg.name %>-<%= pkg.version %>.zip" - files: [ - { - expand: true - cwd: customBuildDest + '/src/' - src: "**/*" - } - ] + compress: tasks.compress(paths, customBuildDest) concat: core: diff --git a/lib/grunt/tasks/compress.js b/lib/grunt/tasks/compress.js new file mode 100644 index 000000000..277766173 --- /dev/null +++ b/lib/grunt/tasks/compress.js @@ -0,0 +1,91 @@ +/* jshint node: true */ +module.exports = function(paths, customBuildDest) { + "use strict"; + + return { + jquery: { + options: { + archive: "" + paths.dist + "/jquery.<%= pkg.name %>-<%= pkg.version %>.zip" + }, + files: [ + { + expand: true, + cwd: paths.dist, + src: "./jquery.<%= pkg.name %>-<%= pkg.version %>/*" + } + ] + }, + jqueryS3: { + options: { + archive: "" + paths.dist + "/s3.jquery.<%= pkg.name %>-<%= pkg.version %>.zip" + }, + files: [ + { + expand: true, + cwd: paths.dist, + src: "./s3.jquery.<%= pkg.name %>-<%= pkg.version %>/*" + } + ] + }, + jqueryAzure: { + options: { + archive: "" + paths.dist + "/azure.jquery.<%= pkg.name %>-<%= pkg.version %>.zip" + }, + files: [ + { + expand: true, + cwd: paths.dist, + src: "./azure.jquery.<%= pkg.name %>-<%= pkg.version %>/*" + } + ] + }, + core: { + options: { + archive: "" + paths.dist + "/<%= pkg.name %>-<%= pkg.version %>.zip" + }, + files: [ + { + expand: true, + cwd: paths.dist, + src: "./<%= pkg.name %>-<%= pkg.version %>/*" + } + ] + }, + coreS3: { + options: { + archive: "" + paths.dist + "/s3.<%= pkg.name %>-<%= pkg.version %>.zip" + }, + files: [ + { + expand: true, + cwd: paths.dist, + src: "./s3.<%= pkg.name %>-<%= pkg.version %>/*" + } + ] + }, + coreAzure: { + options: { + archive: "" + paths.dist + "/azure.<%= pkg.name %>-<%= pkg.version %>.zip" + }, + files: [ + { + expand: true, + cwd: paths.dist, + src: "./azure.<%= pkg.name %>-<%= pkg.version %>/*" + } + ] + }, + custom: { + options: { + archive: "" + customBuildDest + "/custom.<%= pkg.name %>-<%= pkg.version %>.zip" + }, + files: [ + { + expand: true, + cwd: customBuildDest + "/src/", + src: "**/*" + } + ] + } + }; +}; diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index 23d8c8cf0..e1b2242e7 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -1,5 +1,6 @@ /* jshint node: true */ module.exports = module = { clean: require("./clean"), + compress: require("./compress"), }; From d5239f69fed7d3f6a875ed630efe64f5f57239c2 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 19:26:23 -0500 Subject: [PATCH 037/137] chore($grunt/tasks): #1112 - Move 'bower' task to own module --- Gruntfile.coffee | 10 +--------- lib/grunt/tasks/bower.js | 19 +++++++++++++++++++ lib/grunt/tasks/index.js | 1 + 3 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 lib/grunt/tasks/bower.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 198347fd6..1a83d83b3 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -55,15 +55,7 @@ module.exports = (grunt) -> pkg: pkg - bower: - install: - options: - targetDir: "#{paths.test}/_vendor" - install: true - cleanTargetDir: true - cleanBowerDir: true - layout: 'byComponent' - + bower: tasks.bower(paths) clean: tasks.clean(paths) compress: tasks.compress(paths, customBuildDest) diff --git a/lib/grunt/tasks/bower.js b/lib/grunt/tasks/bower.js new file mode 100644 index 000000000..63e03e194 --- /dev/null +++ b/lib/grunt/tasks/bower.js @@ -0,0 +1,19 @@ +/* jshint node:true */ +/* globals module */ +var path = require("path"); + +module.exports = function(paths) { + "use strict"; + return { + "test": { + options: { + targetDir: path.join(paths.test, "_vendor"), + install: true, + cleanTargetDir: true, + cleanBowerDir: true, + layout: "byComponent", + } + } + }; +}; + diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index e1b2242e7..47aadf42e 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -1,5 +1,6 @@ /* jshint node: true */ module.exports = module = { + bower: require("./bower"), clean: require("./clean"), compress: require("./compress"), }; From 3aff2c4073e79bb87be094e95ed27a7f244f8686 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 19:31:09 -0500 Subject: [PATCH 038/137] chore($grunt/tasks) #1112 - Remove useless tasks.coffee --- lib/grunt/tasks.coffee | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 lib/grunt/tasks.coffee diff --git a/lib/grunt/tasks.coffee b/lib/grunt/tasks.coffee deleted file mode 100644 index 241c3e0ce..000000000 --- a/lib/grunt/tasks.coffee +++ /dev/null @@ -1,21 +0,0 @@ -util = require './utils' -path = require 'path' -spawn = require('child_process').spawn -sourceModules = require('../modules') - -module.exports = (grunt) -> - - _ = grunt.util._ - grunt.registerTask 'lint', 'Lint, in order, the Gruntfile, sources, and tests.', ['concurrent:lint'] - - grunt.registerTask 'minify', 'Minify the source javascript and css', [ - 'cssmin:all', 'uglify:core', 'uglify:jquery', 'uglify:coreS3', - 'uglify:jqueryS3', 'uglify:jqueryAzure', 'uglify:coreAzure', 'uglify:all'] - - grunt.registerMultiTask 'tests', '** Use ` grunt-test` instead **', -> - util.startKarma.call util, @data, @async() - - grunt.registerTask 'check_pull_req', '', -> - util.checkPullRequest() - - From baf5590f95cec72fb9ceb88f1402688dca9644b7 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 19:40:22 -0500 Subject: [PATCH 039/137] chore($grunt/tasks): #1112 - Move 'concat' task to own module --- Gruntfile.coffee | 27 +----------------------- lib/grunt/tasks/concat.js | 43 +++++++++++++++++++++++++++++++++++++++ lib/grunt/tasks/index.js | 1 + 3 files changed, 45 insertions(+), 26 deletions(-) create mode 100644 lib/grunt/tasks/concat.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 1a83d83b3..89ea82af3 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -58,32 +58,7 @@ module.exports = (grunt) -> bower: tasks.bower(paths) clean: tasks.clean(paths) compress: tasks.compress(paths, customBuildDest) - - concat: - core: - src: fineUploaderModules.mergeModules true, 'fuTraditional' - dest: "#{paths.build}/<%= pkg.name %>.js" - coreS3: - src: fineUploaderModules.mergeModules true, 'fuS3' - dest: "#{paths.build}/s3.<%= pkg.name %>.js" - coreAzure: - src: fineUploaderModules.mergeModules true, 'fuAzure' - dest: "#{paths.build}/azure.<%= pkg.name %>.js" - jquery: - src: fineUploaderModules.mergeModules true, 'fuTraditionalJquery' - dest: "#{paths.build}/jquery.<%= pkg.name %>.js" - jqueryS3: - src: fineUploaderModules.mergeModules true, 'fuS3Jquery' - dest: "#{paths.build}/s3.jquery.<%= pkg.name %>.js" - jqueryAzure: - src: fineUploaderModules.mergeModules true, 'fuAzureJquery' - dest: "#{paths.build}/azure.jquery.<%= pkg.name %>.js" - all: - src: fineUploaderModules.mergeModules true, 'fuAll' - dest: paths.build + "/all.<%= pkg.name %>.js" - css: - src: ["#{paths.src}/*.css"] - dest: "#{paths.build}/<%= pkg.name %>.css" + concat: tasks.concat(paths) connect: root_server: diff --git a/lib/grunt/tasks/concat.js b/lib/grunt/tasks/concat.js new file mode 100644 index 000000000..f464c5bb8 --- /dev/null +++ b/lib/grunt/tasks/concat.js @@ -0,0 +1,43 @@ +/* jshint node: true */ +var path = require("path"); + +var fineUploaderModules = require("../../modules"); + +module.exports = function(paths) { + "use strict"; + + return { + core: { + src: fineUploaderModules.mergeModules(true, "fuTraditional"), + dest: "" + paths.build + "/<%= pkg.name %>.js" + }, + coreS3: { + src: fineUploaderModules.mergeModules(true, "fuS3"), + dest: "" + paths.build + "/s3.<%= pkg.name %>.js" + }, + coreAzure: { + src: fineUploaderModules.mergeModules(true, "fuAzure"), + dest: "" + paths.build + "/azure.<%= pkg.name %>.js" + }, + jquery: { + src: fineUploaderModules.mergeModules(true, "fuTraditionalJquery"), + dest: "" + paths.build + "/jquery.<%= pkg.name %>.js" + }, + jqueryS3: { + src: fineUploaderModules.mergeModules(true, "fuS3Jquery"), + dest: "" + paths.build + "/s3.jquery.<%= pkg.name %>.js" + }, + jqueryAzure: { + src: fineUploaderModules.mergeModules(true, "fuAzureJquery"), + dest: "" + paths.build + "/azure.jquery.<%= pkg.name %>.js" + }, + all: { + src: fineUploaderModules.mergeModules(true, "fuAll"), + dest: paths.build + "/all.<%= pkg.name %>.js" + }, + css: { + src: ["" + paths.src + "/*.css"], + dest: "" + paths.build + "/<%= pkg.name %>.css" + } + }; +}; diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index 47aadf42e..87cdf1b2b 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -3,5 +3,6 @@ module.exports = module = { bower: require("./bower"), clean: require("./clean"), compress: require("./compress"), + concat: require("./concat"), }; From f32a6e8c2f5dd79a43d2df028fdcd6a5fb198572 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 19:53:32 -0500 Subject: [PATCH 040/137] chore($grunt/tasks): #1112 - Remove unused connect task --- Gruntfile.coffee | 13 ------------- package.json | 1 - 2 files changed, 14 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 89ea82af3..00e00fa6f 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -60,19 +60,6 @@ module.exports = (grunt) -> compress: tasks.compress(paths, customBuildDest) concat: tasks.concat(paths) - connect: - root_server: - options: - base: "." - hostname: "0.0.0.0" - port: 9000 - keepalive: true - test_server: - options: - base: "test" - hostname: "0.0.0.0" - port: 9000 - copy: dist: files: [ diff --git a/package.json b/package.json index f89ed62c6..9e6bc4fc8 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "grunt-contrib-clean": "~0.4.1", "grunt-contrib-compress": "~0.5.2", "grunt-contrib-concat": "~0.3.0", - "grunt-contrib-connect": "~0.3.0", "grunt-contrib-copy": "~0.4.1", "grunt-contrib-cssmin": "~0.6.1", "grunt-contrib-jshint": "~0.7.1", From f551c70973de4c336f3bcbd9ebd7b5c818ce2269 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 19:56:23 -0500 Subject: [PATCH 041/137] chore(/tasks): #1112 - Move 'copy' task to own module --- Gruntfile.coffee | 350 +-------------------------------------- lib/grunt/tasks/copy.js | 315 +++++++++++++++++++++++++++++++++++ lib/grunt/tasks/index.js | 1 + 3 files changed, 317 insertions(+), 349 deletions(-) create mode 100644 lib/grunt/tasks/copy.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 00e00fa6f..ff94805e6 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -59,355 +59,7 @@ module.exports = (grunt) -> clean: tasks.clean(paths) compress: tasks.compress(paths, customBuildDest) concat: tasks.concat(paths) - - copy: - dist: - files: [ - { - expand: true - cwd: paths.build - src: ['*.js', '!all.*', '!s3.*', '!azure.*', '!*.min.js', '!jquery*', '!*iframe*'] - dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/" - ext: '-<%= pkg.version %>.js' - }, - { - expand: true - cwd: paths.build - src: [ '!all.*', 's3.*.js', '!*.min.js', '!s3.jquery*', '!azure.jquery*', '!*iframe*'] - dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.<%= pkg.name %>-<%= pkg.version %>.js' - }, - { - expand: true - cwd: paths.build - src: [ '!all.*', 'azure.*.js', '!*.min.js', '!azure.jquery*', '!s3.jquery*', '!*iframe*'] - dest: "#{paths.dist}/azure.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.<%= pkg.name %>-<%= pkg.version %>.js' - }, - { - expand: true - cwd: paths.build - src: ['*.min.js', '!all.*', '!s3.*', '!azure.*', '!jquery*'] - dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/" - ext: '-<%= pkg.version %>.min.js' - }, - { - expand: true - cwd: paths.build - src: ['s3.*.min.js', '!s3.jquery*'] - dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.<%= pkg.name %>-<%= pkg.version %>.min.js' - }, - { - expand: true - cwd: paths.build - src: ['azure.*.min.js', '!azure.jquery*'] - dest: "#{paths.dist}/azure.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.<%= pkg.name %>-<%= pkg.version %>.min.js' - }, - { - expand: true - cwd: paths.build - src: ['jquery*js', '!s3.*', '!azure.*', '!*.min.js'] - dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.<%= pkg.name %>-<%= pkg.version %>.js' - }, - { - expand: true - cwd: paths.build - src: ['s3.jquery*js', '!*.min.js'] - dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.jquery.<%= pkg.name %>-<%= pkg.version %>.js' - }, - { - expand: true - cwd: paths.build - src: ['azure.jquery*js', '!*.min.js'] - dest: "#{paths.dist}/azure.jquery.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.jquery.<%= pkg.name %>-<%= pkg.version %>.js' - }, - { - expand: true - cwd: paths.build - src: ['jquery*min.js'] - dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.<%= pkg.name %>-<%= pkg.version %>.min.js' - }, - { - expand: true - cwd: paths.build - src: ['s3.jquery*min.js'] - dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.jquery.<%= pkg.name %>-<%= pkg.version %>.min.js' - }, - { - expand: true - cwd: paths.build - src: ['azure.jquery*min.js'] - dest: "#{paths.dist}/azure.jquery.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.jquery.<%= pkg.name %>-<%= pkg.version %>.min.js' - }, - { - expand: true - cwd: "./#{paths.src}/js/" - src: ['iframe.xss.response.js'] - dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/" - ext: '.xss.response-<%= pkg.version %>.js' - }, - { - expand: true - cwd: "./#{paths.src}/js/" - src: ['iframe.xss.response.js'] - dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.xss.response-<%= pkg.version %>.js' - }, - { - expand: true - cwd: "./#{paths.src}/js/" - src: ['iframe.xss.response.js'] - dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.xss.response-<%= pkg.version %>.js' - }, - { - expand: true - cwd: "./#{paths.src}/js/" - src: ['iframe.xss.response.js'] - dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/" - ext: '.xss.response-<%= pkg.version %>.js' - }, - { - expand: true - cwd: paths.src - src: ['*.gif', 'placeholders/*.png'] - dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: paths.src - src: ['*.gif', 'placeholders/*.png'] - dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: paths.src - src: ['*.gif', 'placeholders/*.png'] - dest: "#{paths.dist}/azure.<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: paths.src - src: ['*.gif', 'placeholders/*.png'] - dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: paths.src - src: ['*.gif', 'placeholders/*.png'] - dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: paths.src - src: ['*.gif', 'placeholders/*.png'] - dest: "#{paths.dist}/azure.jquery.<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: './' - src: ['LICENSE'] - dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: './' - src: ['LICENSE'] - dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: './' - src: ['LICENSE'] - dest: "#{paths.dist}/azure.<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: './' - src: ['LICENSE'] - dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: './' - src: ['LICENSE'] - dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: './' - src: ['LICENSE'] - dest: "#{paths.dist}/azure.jquery.<%= pkg.name %>-<%= pkg.version %>/" - }, - { - expand: true - cwd: paths.build - src: ['*.min.css'] - dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.min.css' - }, - { - expand: true - cwd: paths.build - src: ['*.min.css'] - dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.min.css' - }, - { - expand: true - cwd: paths.build - src: ['*.min.css'] - dest: "#{paths.dist}/azure.<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.min.css' - }, - { - expand: true - cwd: paths.build - src: ['*.css', '!*.min.css'] - dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.css' - }, - { - expand: true - cwd: paths.build - src: ['*.css', '!*.min.css'] - dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.css' - }, - { - expand: true - cwd: paths.build - src: ['*.css', '!*.min.css'] - dest: "#{paths.dist}/azure.<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.css' - }, - { - expand: true - cwd: paths.build - src: ['*.min.css'] - dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.min.css' - }, - { - expand: true - cwd: paths.build - src: ['*.min.css'] - dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.min.css' - }, - { - expand: true - cwd: paths.build - src: ['*.min.css'] - dest: "#{paths.dist}/azure.jquery.<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.min.css' - }, - { - expand: true - cwd: paths.build - src: ['*.css', '!*.min.css'] - dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.css' - }, - { - expand: true - cwd: paths.build - src: ['*.css', '!*.min.css'] - dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.css' - }, - { - expand: true - cwd: paths.build - src: ['*.css', '!*.min.css'] - dest: "#{paths.dist}/azure.jquery.<%= pkg.name %>-<%= pkg.version %>" - ext: '-<%= pkg.version %>.css' - }, - { - expand: true - cwd: paths.html - src: ['*.html'] - dest: "#{paths.dist}/<%= pkg.name %>-<%= pkg.version %>/templates/" - }, - { - expand: true - cwd: paths.html - src: ['*.html'] - dest: "#{paths.dist}/s3.<%= pkg.name %>-<%= pkg.version %>/templates/" - }, - { - expand: true - cwd: paths.html - src: ['*.html'] - dest: "#{paths.dist}/azure.<%= pkg.name %>-<%= pkg.version %>/templates/" - }, - { - expand: true - cwd: paths.html - src: ['*.html'] - dest: "#{paths.dist}/jquery.<%= pkg.name %>-<%= pkg.version %>/templates/" - }, - { - expand: true - cwd: paths.html - src: ['*.html'] - dest: "#{paths.dist}/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/templates/" - }, - { - expand: true - cwd: paths.html - src: ['*.html'] - dest: "#{paths.dist}/azure.jquery.<%= pkg.name %>-<%= pkg.version %>/templates/" - } - ] - build: - files: [ - { - expand: true - cwd: "#{paths.src}/js/" - src: ['iframe.xss.response.js'] - dest: paths.build - }, - { - expand: true - cwd: paths.src - src: ['*.gif', 'placeholders/*.png'] - dest: paths.build - } - { - expand: true - cwd: paths.html - src: ['*.html'] - dest: paths.build - } - ] - test: - expand: true - flatten: true - src: ["#{paths.build}/*"] - dest: "#{paths.test}/_temp" - images: - files: [ - expand: true - cwd: paths.src - src: ['*.gif', 'placeholders/*.png'] - dest: paths.build - ] - templates: - files: [ - expand: true - cwd: paths.src + '/html' - src: ['*.html'] - dest: paths.build - ] + copy: tasks.copy(paths) cssmin: options: diff --git a/lib/grunt/tasks/copy.js b/lib/grunt/tasks/copy.js new file mode 100644 index 000000000..3e4b29907 --- /dev/null +++ b/lib/grunt/tasks/copy.js @@ -0,0 +1,315 @@ +/* jshint node: true */ +module.exports = function(paths) { + "use strict"; + return { + dist: { + files: [ + { + expand: true, + cwd: paths.build, + src: ["*.js", "!all.*", "!s3.*", "!azure.*", "!*.min.js", "!jquery*", "!*iframe*"], + dest: "" + paths.dist + "/<%= pkg.name %>-<%= pkg.version %>/", + ext: "-<%= pkg.version %>.js" + }, { + expand: true, + cwd: paths.build, + src: ["!all.*", "s3.*.js", "!*.min.js", "!s3.jquery*", "!azure.jquery*", "!*iframe*"], + dest: "" + paths.dist + "/s3.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".<%= pkg.name %>-<%= pkg.version %>.js" + }, { + expand: true, + cwd: paths.build, + src: ["!all.*", "azure.*.js", "!*.min.js", "!azure.jquery*", "!s3.jquery*", "!*iframe*"], + dest: "" + paths.dist + "/azure.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".<%= pkg.name %>-<%= pkg.version %>.js" + }, { + expand: true, + cwd: paths.build, + src: ["*.min.js", "!all.*", "!s3.*", "!azure.*", "!jquery*"], + dest: "" + paths.dist + "/<%= pkg.name %>-<%= pkg.version %>/", + ext: "-<%= pkg.version %>.min.js" + }, { + expand: true, + cwd: paths.build, + src: ["s3.*.min.js", "!s3.jquery*"], + dest: "" + paths.dist + "/s3.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".<%= pkg.name %>-<%= pkg.version %>.min.js" + }, { + expand: true, + cwd: paths.build, + src: ["azure.*.min.js", "!azure.jquery*"], + dest: "" + paths.dist + "/azure.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".<%= pkg.name %>-<%= pkg.version %>.min.js" + }, { + expand: true, + cwd: paths.build, + src: ["jquery*js", "!s3.*", "!azure.*", "!*.min.js"], + dest: "" + paths.dist + "/jquery.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".<%= pkg.name %>-<%= pkg.version %>.js" + }, { + expand: true, + cwd: paths.build, + src: ["s3.jquery*js", "!*.min.js"], + dest: "" + paths.dist + "/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".jquery.<%= pkg.name %>-<%= pkg.version %>.js" + }, { + expand: true, + cwd: paths.build, + src: ["azure.jquery*js", "!*.min.js"], + dest: "" + paths.dist + "/azure.jquery.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".jquery.<%= pkg.name %>-<%= pkg.version %>.js" + }, { + expand: true, + cwd: paths.build, + src: ["jquery*min.js"], + dest: "" + paths.dist + "/jquery.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".<%= pkg.name %>-<%= pkg.version %>.min.js" + }, { + expand: true, + cwd: paths.build, + src: ["s3.jquery*min.js"], + dest: "" + paths.dist + "/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".jquery.<%= pkg.name %>-<%= pkg.version %>.min.js" + }, { + expand: true, + cwd: paths.build, + src: ["azure.jquery*min.js"], + dest: "" + paths.dist + "/azure.jquery.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".jquery.<%= pkg.name %>-<%= pkg.version %>.min.js" + }, { + expand: true, + cwd: "./" + paths.src + "/js/", + src: ["iframe.xss.response.js"], + dest: "" + paths.dist + "/<%= pkg.name %>-<%= pkg.version %>/", + ext: ".xss.response-<%= pkg.version %>.js" + }, { + expand: true, + cwd: "./" + paths.src + "/js/", + src: ["iframe.xss.response.js"], + dest: "" + paths.dist + "/s3.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".xss.response-<%= pkg.version %>.js" + }, { + expand: true, + cwd: "./" + paths.src + "/js/", + src: ["iframe.xss.response.js"], + dest: "" + paths.dist + "/jquery.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".xss.response-<%= pkg.version %>.js" + }, { + expand: true, + cwd: "./" + paths.src + "/js/", + src: ["iframe.xss.response.js"], + dest: "" + paths.dist + "/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/", + ext: ".xss.response-<%= pkg.version %>.js" + }, { + expand: true, + cwd: paths.src, + src: ["*.gif", "placeholders/*.png"], + dest: "" + paths.dist + "/<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: paths.src, + src: ["*.gif", "placeholders/*.png"], + dest: "" + paths.dist + "/s3.<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: paths.src, + src: ["*.gif", "placeholders/*.png"], + dest: "" + paths.dist + "/azure.<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: paths.src, + src: ["*.gif", "placeholders/*.png"], + dest: "" + paths.dist + "/jquery.<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: paths.src, + src: ["*.gif", "placeholders/*.png"], + dest: "" + paths.dist + "/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: paths.src, + src: ["*.gif", "placeholders/*.png"], + dest: "" + paths.dist + "/azure.jquery.<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: "./", + src: ["LICENSE"], + dest: "" + paths.dist + "/<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: "./", + src: ["LICENSE"], + dest: "" + paths.dist + "/s3.<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: "./", + src: ["LICENSE"], + dest: "" + paths.dist + "/azure.<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: "./", + src: ["LICENSE"], + dest: "" + paths.dist + "/jquery.<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: "./", + src: ["LICENSE"], + dest: "" + paths.dist + "/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: "./", + src: ["LICENSE"], + dest: "" + paths.dist + "/azure.jquery.<%= pkg.name %>-<%= pkg.version %>/" + }, { + expand: true, + cwd: paths.build, + src: ["*.min.css"], + dest: "" + paths.dist + "/<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.min.css" + }, { + expand: true, + cwd: paths.build, + src: ["*.min.css"], + dest: "" + paths.dist + "/s3.<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.min.css" + }, { + expand: true, + cwd: paths.build, + src: ["*.min.css"], + dest: "" + paths.dist + "/azure.<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.min.css" + }, { + expand: true, + cwd: paths.build, + src: ["*.css", "!*.min.css"], + dest: "" + paths.dist + "/<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.css" + }, { + expand: true, + cwd: paths.build, + src: ["*.css", "!*.min.css"], + dest: "" + paths.dist + "/s3.<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.css" + }, { + expand: true, + cwd: paths.build, + src: ["*.css", "!*.min.css"], + dest: "" + paths.dist + "/azure.<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.css" + }, { + expand: true, + cwd: paths.build, + src: ["*.min.css"], + dest: "" + paths.dist + "/jquery.<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.min.css" + }, { + expand: true, + cwd: paths.build, + src: ["*.min.css"], + dest: "" + paths.dist + "/s3.jquery.<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.min.css" + }, { + expand: true, + cwd: paths.build, + src: ["*.min.css"], + dest: "" + paths.dist + "/azure.jquery.<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.min.css" + }, { + expand: true, + cwd: paths.build, + src: ["*.css", "!*.min.css"], + dest: "" + paths.dist + "/jquery.<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.css" + }, { + expand: true, + cwd: paths.build, + src: ["*.css", "!*.min.css"], + dest: "" + paths.dist + "/s3.jquery.<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.css" + }, { + expand: true, + cwd: paths.build, + src: ["*.css", "!*.min.css"], + dest: "" + paths.dist + "/azure.jquery.<%= pkg.name %>-<%= pkg.version %>", + ext: "-<%= pkg.version %>.css" + }, { + expand: true, + cwd: paths.html, + src: ["*.html"], + dest: "" + paths.dist + "/<%= pkg.name %>-<%= pkg.version %>/templates/" + }, { + expand: true, + cwd: paths.html, + src: ["*.html"], + dest: "" + paths.dist + "/s3.<%= pkg.name %>-<%= pkg.version %>/templates/" + }, { + expand: true, + cwd: paths.html, + src: ["*.html"], + dest: "" + paths.dist + "/azure.<%= pkg.name %>-<%= pkg.version %>/templates/" + }, { + expand: true, + cwd: paths.html, + src: ["*.html"], + dest: "" + paths.dist + "/jquery.<%= pkg.name %>-<%= pkg.version %>/templates/" + }, { + expand: true, + cwd: paths.html, + src: ["*.html"], + dest: "" + paths.dist + "/s3.jquery.<%= pkg.name %>-<%= pkg.version %>/templates/" + }, { + expand: true, + cwd: paths.html, + src: ["*.html"], + dest: "" + paths.dist + "/azure.jquery.<%= pkg.name %>-<%= pkg.version %>/templates/" + } + ] + }, + build: { + files: [ + { + expand: true, + cwd: "" + paths.src + "/js/", + src: ["iframe.xss.response.js"], + dest: paths.build + }, { + expand: true, + cwd: paths.src, + src: ["*.gif", "placeholders/*.png"], + dest: paths.build + }, { + expand: true, + cwd: paths.html, + src: ["*.html"], + dest: paths.build + } + ] + }, + test: { + expand: true, + flatten: true, + src: ["" + paths.build + "/*"], + dest: "" + paths.test + "/_temp" + }, + images: { + files: [ + { + expand: true, + cwd: paths.src, + src: ["*.gif", "placeholders/*.png"], + dest: paths.build + } + ] + }, + templates: { + files: [ + { + expand: true, + cwd: paths.src + "/html", + src: ["*.html"], + dest: paths.build + } + ] + } + }; +}; diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index 87cdf1b2b..7c6323eb7 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -4,5 +4,6 @@ module.exports = module = { clean: require("./clean"), compress: require("./compress"), concat: require("./concat"), + copy: require("./copy"), }; From 32677e422d2a70fb59e2969a87ddd7447712f230 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 19:59:23 -0500 Subject: [PATCH 042/137] chore(/tasks): #1112 - Move 'cssmin' task to own module --- Gruntfile.coffee | 20 +------------------- lib/grunt/tasks/cssmin.js | 24 ++++++++++++++++++++++++ lib/grunt/tasks/index.js | 1 + 3 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 lib/grunt/tasks/cssmin.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index ff94805e6..aeecf16be 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -60,25 +60,7 @@ module.exports = (grunt) -> compress: tasks.compress(paths, customBuildDest) concat: tasks.concat(paths) copy: tasks.copy(paths) - - cssmin: - options: - banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' - report: 'min' - all: - expand: true - cwd: paths.build - src: ['*.css', '!*.min.css'] - dest: paths.build - ext: ".min.css" - custom: - expand: true - cwd: customBuildDest + '/src/' - src: ['*.css', '!*.min.css'] - dest: customBuildDest + '/src/' - ext: '.<%= pkg.name %>-<%= pkg.version %>.min.css' - #src: ["#{customBuildDest}/src/<%= pkg.name %>-<%= pkg.version %>.css"] - #dest: "#{customBuildDest}/src/<%= pkg.name %>-<%= pkg.version %>.min.css" + cssmin: tasks.cssmin(paths, customBuildDest) jshint: source: ["#{paths.src}/js/**/*.js"] diff --git a/lib/grunt/tasks/cssmin.js b/lib/grunt/tasks/cssmin.js new file mode 100644 index 000000000..ce441e2a5 --- /dev/null +++ b/lib/grunt/tasks/cssmin.js @@ -0,0 +1,24 @@ +/* jshint node: true */ +module.exports = function(paths, customBuildDest) { + "use strict"; + return { + options: { + banner: "/*! <%= pkg.name %> <%= grunt.template.today('yyyy-mm-dd') %> */\n", + report: "min" + }, + all: { + expand: true, + cwd: paths.build, + src: ["*.css", "!*.min.css"], + dest: paths.build, + ext: ".min.css" + }, + custom: { + expand: true, + cwd: customBuildDest + "/src/", + src: ["*.css", "!*.min.css"], + dest: customBuildDest + "/src/", + ext: ".<%= pkg.name %>-<%= pkg.version %>.min.css" + } + }; +}; diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index 7c6323eb7..5d6c5bf45 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -5,5 +5,6 @@ module.exports = module = { compress: require("./compress"), concat: require("./concat"), copy: require("./copy"), + cssmin: require("./cssmin"), }; From 225ad32b770868f41e8eff586f5056f0bd94812e Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 20:03:45 -0500 Subject: [PATCH 043/137] chore(/tasks): #1112 - Move 'jshint' task to own module --- Gruntfile.coffee | 8 +------- lib/grunt/tasks/index.js | 1 + lib/grunt/tasks/jshint.js | 12 ++++++++++++ 3 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 lib/grunt/tasks/jshint.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index aeecf16be..5355204f0 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -61,13 +61,7 @@ module.exports = (grunt) -> concat: tasks.concat(paths) copy: tasks.copy(paths) cssmin: tasks.cssmin(paths, customBuildDest) - - jshint: - source: ["#{paths.src}/js/**/*.js"] - tests: ["#{paths.test}/unit/**/*.js","#{paths.test}/static/local/*.js"] - options: - jshintrc: true - ignores: ["#{paths.src}/js/third-party/**/*.js"] + jshint: tasks.jshint(paths) custom: options: diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index 5d6c5bf45..51171c446 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -6,5 +6,6 @@ module.exports = module = { concat: require("./concat"), copy: require("./copy"), cssmin: require("./cssmin"), + jshint: require("./jshint"), }; diff --git a/lib/grunt/tasks/jshint.js b/lib/grunt/tasks/jshint.js new file mode 100644 index 000000000..4a28f13b3 --- /dev/null +++ b/lib/grunt/tasks/jshint.js @@ -0,0 +1,12 @@ +/* jshint node: true */ +module.exports = function(paths) { + "use strict"; + return { + source: ["" + paths.src + "/js/**/*.js"], + tests: ["" + paths.test + "/unit/**/*.js", "" + paths.test + "/static/local/*.js"], + options: { + jshintrc: true, + ignores: ["" + paths.src + "/js/third-party/**/*.js"] + } + }; +}; From f2999f556fd9017cca6b1985af09872d21720390 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 20:06:20 -0500 Subject: [PATCH 044/137] chore(/tasks): #1112 - Move 'uglify' task to own module --- Gruntfile.coffee | 32 +-------------------------- lib/grunt/tasks/index.js | 1 + lib/grunt/tasks/uglify.js | 46 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 31 deletions(-) create mode 100644 lib/grunt/tasks/uglify.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 5355204f0..a27ea313e 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -62,42 +62,12 @@ module.exports = (grunt) -> copy: tasks.copy(paths) cssmin: tasks.cssmin(paths, customBuildDest) jshint: tasks.jshint(paths) + uglify: tasks.uglify(paths, customBuildDest) custom: options: dest: customBuildDest - uglify: - options: - mangle: true - compress: - warnings: false - report: 'min' - preserveComments: 'some' - core: - src: ['<%= concat.core.dest %>'] - dest: "#{paths.build}/<%= pkg.name %>.min.js" - jquery: - src: ['<%= concat.jquery.dest %>'] - dest: "#{paths.build}/jquery.<%= pkg.name %>.min.js" - coreAzure: - src: ['<%= concat.coreAzure.dest %>'] - dest: "#{paths.build}/azure.<%= pkg.name %>.min.js" - jqueryAzure: - src: ['<%= concat.jqueryAzure.dest %>'] - dest: "#{paths.build}/azure.jquery.<%= pkg.name %>.min.js" - coreS3: - src: ['<%= concat.coreS3.dest %>'] - dest: "#{paths.build}/s3.<%= pkg.name %>.min.js" - jqueryS3: - src: ['<%= concat.jqueryS3.dest %>'] - dest: "#{paths.build}/s3.jquery.<%= pkg.name %>.min.js" - all: - src: ['<%= concat.all.dest %>'] - dest: "#{paths.build}/all.<%= pkg.name %>.min.js" - custom: - src: ["#{customBuildDest}/src/custom.<%= pkg.name %>-<%= pkg.version %>.js"] - dest: "#{customBuildDest}/src/custom.<%= pkg.name %>-<%= pkg.version %>.min.js" usebanner: allhead: diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index 51171c446..86eb7fd69 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -7,5 +7,6 @@ module.exports = module = { copy: require("./copy"), cssmin: require("./cssmin"), jshint: require("./jshint"), + uglify: require("./uglify"), }; diff --git a/lib/grunt/tasks/uglify.js b/lib/grunt/tasks/uglify.js new file mode 100644 index 000000000..95dbf1691 --- /dev/null +++ b/lib/grunt/tasks/uglify.js @@ -0,0 +1,46 @@ +/* jshint node: true */ +module.exports = function(paths, customBuildDest) { + "use strict"; + return { + options: { + mangle: true, + compress: { + warnings: false + }, + report: "min", + preserveComments: "some" + }, + core: { + src: ["<%= concat.core.dest %>"], + dest: "" + paths.build + "/<%= pkg.name %>.min.js" + }, + jquery: { + src: ["<%= concat.jquery.dest %>"], + dest: "" + paths.build + "/jquery.<%= pkg.name %>.min.js" + }, + coreAzure: { + src: ["<%= concat.coreAzure.dest %>"], + dest: "" + paths.build + "/azure.<%= pkg.name %>.min.js" + }, + jqueryAzure: { + src: ["<%= concat.jqueryAzure.dest %>"], + dest: "" + paths.build + "/azure.jquery.<%= pkg.name %>.min.js" + }, + coreS3: { + src: ["<%= concat.coreS3.dest %>"], + dest: "" + paths.build + "/s3.<%= pkg.name %>.min.js" + }, + jqueryS3: { + src: ["<%= concat.jqueryS3.dest %>"], + dest: "" + paths.build + "/s3.jquery.<%= pkg.name %>.min.js" + }, + all: { + src: ["<%= concat.all.dest %>"], + dest: "" + paths.build + "/all.<%= pkg.name %>.min.js" + }, + custom: { + src: ["" + customBuildDest + "/src/custom.<%= pkg.name %>-<%= pkg.version %>.js"], + dest: "" + customBuildDest + "/src/custom.<%= pkg.name %>-<%= pkg.version %>.min.js" + } + }; +}; From 1a8cc1e1f55562c5c60dc006ae9dcf19db85165e Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 20:09:44 -0500 Subject: [PATCH 045/137] chore(/tasks): #1112 - Move 'banner' task to own module --- Gruntfile.coffee | 60 +-------------------------------------- lib/grunt/tasks/banner.js | 38 +++++++++++++++++++++++++ lib/grunt/tasks/index.js | 3 +- 3 files changed, 41 insertions(+), 60 deletions(-) create mode 100644 lib/grunt/tasks/banner.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index a27ea313e..09c006dce 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -63,71 +63,13 @@ module.exports = (grunt) -> cssmin: tasks.cssmin(paths, customBuildDest) jshint: tasks.jshint(paths) uglify: tasks.uglify(paths, customBuildDest) + usebanner: tasks.banner(paths, customBuildDest) custom: options: dest: customBuildDest - usebanner: - allhead: - src: ["#{paths.build}/*.{js,css}"] - options: - position: 'top' - banner: ''' - /*! - * <%= pkg.title %> - * - * Copyright 2013, <%= pkg.author %> info@fineuploader.com - * - * Version: <%= pkg.version %> - * - * Homepage: http://fineuploader.com - * - * Repository: <%= pkg.repository.url %> - * - * Licensed under GNU GPL v3, see LICENSE - */ \n\n''' - allfoot: - src: ["#{paths.build}/*.{js,css}"] - options: - position: 'bottom' - banner: '/*! <%= grunt.template.today("yyyy-mm-dd") %> */\n' - customhead: - files: - src: ["#{customBuildDest}/src/*.{js,css}"] - options: - position: 'top' - banner: ''' - /*! - * <%= pkg.title %> - * - * Copyright 2013-2014, <%= pkg.author %> info@fineuploader.com - * - * Version: <%= pkg.version %> - * - * Homepage: http://fineuploader.com - * - * Repository: <%= pkg.repository.url %> - * - * Licensed under GNU GPL v3, see LICENSE - * - * Third-party credits: - * MegaPixImageModule (MIT) - * https://github.com/stomita/ios-imagefile-megapixel - * Copyright (c) 2012 Shinichi Tomita - * - * CryptoJS - * code.google.com/p/crypto-js/wiki/License - * (c) 2009-2013 by Jeff Mott. All rights reserved. - */ \n\n''' - customfoot: - files: - src: ["#{customBuildDest}/*.{js,css}"] - options: - position: 'bottom' - banner: '/*! <%= grunt.template.today("yyyy-mm-dd") %> */\n' - version: options: pkg: pkg, diff --git a/lib/grunt/tasks/banner.js b/lib/grunt/tasks/banner.js new file mode 100644 index 000000000..4997606bb --- /dev/null +++ b/lib/grunt/tasks/banner.js @@ -0,0 +1,38 @@ +/* jshint node: true */ +module.exports = function(paths, customBuildDest) { + "use strict"; + return { + allhead: { + src: ["" + paths.build + "/*.{js,css}"], + options: { + position: "top", + banner: "/*!\n* <%= pkg.title %>\n*\n* Copyright 2013, <%= pkg.author %> info@fineuploader.com\n*\n* Version: <%= pkg.version %>\n*\n* Homepage: http://fineuploader.com\n*\n* Repository: <%= pkg.repository.url %>\n*\n* Licensed under GNU GPL v3, see LICENSE\n*/ \n\n" + } + }, + allfoot: { + src: ["" + paths.build + "/*.{js,css}"], + options: { + position: "bottom", + banner: "/*! <%= grunt.template.today('yyyy-mm-dd') %> */\n" + } + }, + customhead: { + files: { + src: ["" + customBuildDest + "/src/*.{js,css}"] + }, + options: { + position: "top", + banner: "/*!\n* <%= pkg.title %>\n*\n* Copyright 2013-2014, <%= pkg.author %> info@fineuploader.com\n*\n* Version: <%= pkg.version %>\n*\n* Homepage: http://fineuploader.com\n*\n* Repository: <%= pkg.repository.url %>\n*\n* Licensed under GNU GPL v3, see LICENSE\n*\n* Third-party credits:\n* MegaPixImageModule (MIT)\n* https://github.com/stomita/ios-imagefile-megapixel\n* Copyright (c) 2012 Shinichi Tomita \n*\n* CryptoJS\n* code.google.com/p/crypto-js/wiki/License\n* (c) 2009-2013 by Jeff Mott. All rights reserved.\n*/ \n\n" + } + }, + customfoot: { + files: { + src: ["" + customBuildDest + "/*.{js,css}"] + }, + options: { + position: "bottom", + banner: "/*! <%= grunt.template.today('yyyy-mm-dd') %> */\n" + } + } + } +}; diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index 86eb7fd69..3c98d7850 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -1,5 +1,6 @@ /* jshint node: true */ -module.exports = module = { +module.exports = { + banner: require("./banner"), bower: require("./bower"), clean: require("./clean"), compress: require("./compress"), From e7a122e85f10d011403525fbcc0c789d24b0ad17 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 20:14:22 -0500 Subject: [PATCH 046/137] chore(/tasks): #1112 - Move 'version' task to own module --- Gruntfile.coffee | 26 +---------------------- lib/grunt/tasks/index.js | 1 + lib/grunt/tasks/version.js | 42 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 lib/grunt/tasks/version.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 09c006dce..e291d7669 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -64,37 +64,13 @@ module.exports = (grunt) -> jshint: tasks.jshint(paths) uglify: tasks.uglify(paths, customBuildDest) usebanner: tasks.banner(paths, customBuildDest) + version: tasks.version(pkg) custom: options: dest: customBuildDest - version: - options: - pkg: pkg, - prefix: '[^\\-][Vv]ersion[\'"]?\\s*[:=]\\s*[\'"]?' - major: - options: - release: 'major' - src: fineUploaderModules.modules.versioned - minor: - options: - release: 'minor' - src: fineUploaderModules.modules.versioned - hotfix: - options: - release: 'patch' - src: fineUploaderModules.modules.versioned - build: - options: - release: 'build' - src: fineUploaderModules.modules.versioned - release: - options: - release: pkg.version.replace /-\d+$/, "" - src: fineUploaderModules.modules.versioned - watch: options: interrupt: true diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index 3c98d7850..17704adfe 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -9,5 +9,6 @@ module.exports = { cssmin: require("./cssmin"), jshint: require("./jshint"), uglify: require("./uglify"), + version: require("./version"), }; diff --git a/lib/grunt/tasks/version.js b/lib/grunt/tasks/version.js new file mode 100644 index 000000000..c9169729b --- /dev/null +++ b/lib/grunt/tasks/version.js @@ -0,0 +1,42 @@ +/* jshint node: true */ +var fineUploaderModules = require("../../modules"); + +module.exports = function(pkg) { + "use strict"; + return { + options: { + pkg: pkg, + prefix: '[^\\-][Vv]ersion[\'"]?\\s*[:=]\\s*[\'"]?' + }, + major: { + options: { + release: "major" + }, + src: fineUploaderModules.modules.versioned + }, + minor: { + options: { + release: "minor" + }, + src: fineUploaderModules.modules.versioned + }, + hotfix: { + options: { + release: "patch" + }, + src: fineUploaderModules.modules.versioned + }, + build: { + options: { + release: "build" + }, + src: fineUploaderModules.modules.versioned + }, + release: { + options: { + release: pkg.version.replace(/-\d+$/, "") + }, + src: fineUploaderModules.modules.versioned + } + }; +}; From 5584844c7685e0dcb23decb48754e4299c24d231 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 20:16:41 -0500 Subject: [PATCH 047/137] chore(/tasks): #1112 - Move 'watch' task to own module --- Gruntfile.coffee | 24 +----------------------- lib/grunt/tasks/index.js | 1 + lib/grunt/tasks/watch.js | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 lib/grunt/tasks/watch.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index e291d7669..6309c33e5 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -65,34 +65,12 @@ module.exports = (grunt) -> uglify: tasks.uglify(paths, customBuildDest) usebanner: tasks.banner(paths, customBuildDest) version: tasks.version(pkg) + watch: tasks.watch(paths) custom: options: dest: customBuildDest - - watch: - options: - interrupt: true - debounceDelay: 250 - js: - files: ["#{paths.src}/js/*.js", "#{paths.src}/js/s3/*.js"] - tasks: [ - 'dev' - 'tests:local' - ] - test: - files: ["#{paths.test}/unit/*.js", "#{paths.test}/unit/s3/*.js"] - tasks: [ - 'jshint:tests' - 'tests:local' - ] - images: - files: ["#{paths.src}/*.gif", "#{paths.src}/placeholders/*.png"] - tasks: [ - 'copy:images' - ] - tests: local: 'karma-local.conf.coffee' travis: 'karma-travis.conf.coffee' diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index 17704adfe..3d60c021d 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -10,5 +10,6 @@ module.exports = { jshint: require("./jshint"), uglify: require("./uglify"), version: require("./version"), + watch: require("./watch"), }; diff --git a/lib/grunt/tasks/watch.js b/lib/grunt/tasks/watch.js new file mode 100644 index 000000000..f2dbf5302 --- /dev/null +++ b/lib/grunt/tasks/watch.js @@ -0,0 +1,22 @@ +/* jshint node: true */ +module.exports = function(paths) { + "use strict"; + return { + options: { + interrupt: true, + debounceDelay: 250 + }, + js: { + files: ["" + paths.src + "/js/*.js", "" + paths.src + "/js/s3/*.js"], + tasks: ["dev", "tests:local"] + }, + test: { + files: ["" + paths.test + "/unit/*.js", "" + paths.test + "/unit/s3/*.js"], + tasks: ["jshint:tests", "tests:local"] + }, + images: { + files: ["" + paths.src + "/*.gif", "" + paths.src + "/placeholders/*.png"], + tasks: ["copy:images"] + } + }; +}; From c092d56c428f9e88e8841e9aa0a12d82f4c58138 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 20:18:01 -0500 Subject: [PATCH 048/137] chore($Gruntfile): Remove unused grunt-shell commands --- Gruntfile.coffee | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 6309c33e5..b461d65ee 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -76,12 +76,6 @@ module.exports = (grunt) -> travis: 'karma-travis.conf.coffee' shell: - start_saucecon: - command: './lib/sauce/sauce_connect_setup.sh' - kill_saucecon: - command: 'cat /tmp/sauce-connect.pid | xargs kill' - npm_install: - command: 'npm install' version_custom_templates: command: "find #{customBuildDest}/ -type f -name '*.html' | xargs sed -i '' 's/{VERSION}/<%= pkg.version %>/'" options: From dcc17ce13b8bfa160ca7095406d3cd97a5120d64 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 20:23:52 -0500 Subject: [PATCH 049/137] chore(/tasks): #1112 - Move 'strip_code' task to own module --- Gruntfile.coffee | 21 +-------------------- lib/grunt/tasks/index.js | 1 + lib/grunt/tasks/stripcode.js | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 lib/grunt/tasks/stripcode.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index b461d65ee..ed1a15632 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -62,6 +62,7 @@ module.exports = (grunt) -> copy: tasks.copy(paths) cssmin: tasks.cssmin(paths, customBuildDest) jshint: tasks.jshint(paths) + strip_code: tasks.stripcode(paths, customBuildDest) uglify: tasks.uglify(paths, customBuildDest) usebanner: tasks.banner(paths, customBuildDest) version: tasks.version(pkg) @@ -89,26 +90,6 @@ module.exports = (grunt) -> stderr: true stdout: true - - strip_code: - options: - start_comment: "" - end_comment: "" - build: - files: - src: "#{paths.build}/**/*.js" - custom: - files: - src: "#{customBuildDest}/**/*.js" - - nodestatic: - server: - options: - port: 3000 - base: "test/unit/resources" - headers: - "Access-Control-Allow-Origin": "*" - # Dependencies # ========== for name of pkg.devDependencies when name.substring(0, 6) is 'grunt-' diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index 3d60c021d..4db0dc90c 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -8,6 +8,7 @@ module.exports = { copy: require("./copy"), cssmin: require("./cssmin"), jshint: require("./jshint"), + stripcode: require("./stripcode"), uglify: require("./uglify"), version: require("./version"), watch: require("./watch"), diff --git a/lib/grunt/tasks/stripcode.js b/lib/grunt/tasks/stripcode.js new file mode 100644 index 000000000..884317dfd --- /dev/null +++ b/lib/grunt/tasks/stripcode.js @@ -0,0 +1,20 @@ +/* jshint node: true */ +module.exports = function(paths, customBuildDest) { + "use strict"; + return { + options: { + start_comment: "", + end_comment: "" + }, + build: { + files: { + src: "" + paths.build + "/**/*.js" + } + }, + custom: { + files: { + src: "" + customBuildDest + "/**/*.js" + } + } + }; +}; From 89eb73c3ed681b4804f6b452af0fbd838399af65 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 20:24:21 -0500 Subject: [PATCH 050/137] chore(/tasks): #1112 - Move 'nodestatic' task to own module --- Gruntfile.coffee | 1 + lib/grunt/tasks/index.js | 1 + lib/grunt/tasks/static.js | 15 +++++++++++++++ 3 files changed, 17 insertions(+) create mode 100644 lib/grunt/tasks/static.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index ed1a15632..7b4b0b64f 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -62,6 +62,7 @@ module.exports = (grunt) -> copy: tasks.copy(paths) cssmin: tasks.cssmin(paths, customBuildDest) jshint: tasks.jshint(paths) + nodestatic: tasks.static(paths) strip_code: tasks.stripcode(paths, customBuildDest) uglify: tasks.uglify(paths, customBuildDest) usebanner: tasks.banner(paths, customBuildDest) diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index 4db0dc90c..b01659fd9 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -8,6 +8,7 @@ module.exports = { copy: require("./copy"), cssmin: require("./cssmin"), jshint: require("./jshint"), + static: require("./static"), stripcode: require("./stripcode"), uglify: require("./uglify"), version: require("./version"), diff --git a/lib/grunt/tasks/static.js b/lib/grunt/tasks/static.js new file mode 100644 index 000000000..0c17185ad --- /dev/null +++ b/lib/grunt/tasks/static.js @@ -0,0 +1,15 @@ +/* jshint node: true */ +module.exports = function(paths) { + "use strict"; + return { + server: { + options: { + port: 3000, + base: paths.test + "/unit/resources", + headers: { + "Access-Control-Allow-Origin": "*" + } + } + } + }; +}; From d8d756a5d033d7a076005755a6a722b6ed9f9be1 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 20:46:39 -0500 Subject: [PATCH 051/137] chore($Build): #1112 - Remove saucelabs artifacts --- .gitignore | 2 - .travis.yml | 1 - karma-sauce.conf.coffee | 23 ------- lib/browsers.coffee | 17 ----- lib/grunt/sauce.coffee | 115 ------------------------------- lib/grunt/utils.coffee | 15 ---- lib/sauce/sauce_connect_block.sh | 7 -- lib/sauce/sauce_connect_setup.sh | 76 -------------------- test/README.md | 45 ------------ test/functional/spike.coffee | 31 --------- 10 files changed, 332 deletions(-) delete mode 100644 karma-sauce.conf.coffee delete mode 100644 lib/grunt/sauce.coffee delete mode 100755 lib/sauce/sauce_connect_block.sh delete mode 100755 lib/sauce/sauce_connect_setup.sh delete mode 100644 test/README.md delete mode 100644 test/functional/spike.coffee diff --git a/.gitignore b/.gitignore index 8080417c7..9f7bf1b5c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,8 +19,6 @@ hardcopy* selenium.log* pid.txt -sauce_connect.log* -lib/sauce/sauce-connect* fine-uploader/ test/upload/* diff --git a/.travis.yml b/.travis.yml index 5c3113860..56e4f080f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ node_js: env: global: - - SAUCE_CONNECT_READY_FILE=/tmp/sauce-connect-ready - LOGS_DIR=/tmp/fineuploader-build/logs - SLIMERJSLAUNCHER=$(which firefox) DISPLAY=:99.0 PATH=$TRAVIS_BUILD_DIR/slimerjs:$PATH SLIMERJS_BIN=$TRAVIS_BUILD_DIR/slimerjs-0.9.0/slimerjs - secure: |- diff --git a/karma-sauce.conf.coffee b/karma-sauce.conf.coffee deleted file mode 100644 index 391619a2a..000000000 --- a/karma-sauce.conf.coffee +++ /dev/null @@ -1,23 +0,0 @@ -# SauceLabs Karma configuration -sharedConfig = require './karma.conf' -modules = require './lib/modules' -allBrowsers = require './lib/browsers' -testRunnerId = if process.env.TRAVIS_BRANCH? then "travis #{process.env.TRAVIS_BRANCH}" else "#{process.env.SAUCE_USERNAME}@local" - -module.exports = (config, options = {}) -> - sharedConfig config, - testName: '[sauce] FineUploader: tests' - logFile: 'fineuploader-sauce.log' - singleRun: true - autoWatch: false - customLaunchers: allBrowsers.sauceBrowsers - sauceLabs: - recordVideo: false - startConnect: false - tags: [ testRunnerId ] - testName: options.testName || '[unit] FineUploader' - username: process.env.SAUCE_USERNAME || process.env.SAUCE_USER_NAME || '' - accessKey: process.env.SAUCE_ACCESS_KEY || process.env.SAUCE_ACCESSKEY || '' - build: process.env.TRAVIS_BUILD_ID || `Math.floor((new Date).getTime() / 1000 - 1230768000).toString()` - tunnelIdentifer: process.env.TRAVIS_JOB_NUMBER || `Math.floor((new Date).getTime() / 1000 - 1230768000).toString()` - diff --git a/lib/browsers.coffee b/lib/browsers.coffee index 8987d88f3..f1cc86ea1 100644 --- a/lib/browsers.coffee +++ b/lib/browsers.coffee @@ -53,20 +53,3 @@ browsers = if (exports) exports.modules = browsers - exports.sauceBrowsers = sauceBrowsers = do -> - b = {} - for browser in browsers - key = "SL-#{browser.browserName.replace " ", "_"}-#{browser.version || ''}-#{browser.platform.replace " ", "_"}" - key = key.replace " ", "_" - b[key] = - base: 'SauceLabs' - browserName: browser.browserName - version: browser.version - platform: browser.platform - return b - - exports.sauceBrowserKeys = do -> - res = [] - for k of sauceBrowsers - res.push k - return res diff --git a/lib/grunt/sauce.coffee b/lib/grunt/sauce.coffee deleted file mode 100644 index 043bc0453..000000000 --- a/lib/grunt/sauce.coffee +++ /dev/null @@ -1,115 +0,0 @@ -#allBrowsers = require './../../browsers' -# -#module.exports = (grunt) -> -# -# mochaWebdriver: -# options: -# timeout: 60000 -# testName: '[selenium] Fine Uploader' -# reporter: 'spec' -# local: -# src: ['test/functional/*.coffee'] -# options: -# usePhantom: true -# sauce: -# src: ['test/functional/*.coffee'] -# options: -# username: process.env.SAUCE_USERNAME || process.env.SAUCE_USER_NAME || '' -# key: process.env.SAUCE_ACCESS_KEY || process.env.SAUCE_ACCESSKEY || '' -# identifier: process.env.TRAVIS_JOB_NUMBER || `Math.floor((new Date).getTime() / 1000 - 1230768000).toString()` -# concurrency: 3 -# tunnelTimeout: 60000 -# browsers: allBrowsers.modules -# -# saucetests: -# default: -# configFile: 'karma-sauce.conf.coffee' -# browsers: [ -# ['SL-android-4.0-Linux', 'SL-iphone-6-OS_X_10.8', 'SL-safari-5-OS_X_10.6'], -# ['SL-chrome-28-Linux', 'SL-firefox-26-Linux', 'SL-safari-6-OS_X_10.8'], -# ['SL-internet_explorer-11-Windows_8.1', 'SL-internet_explorer-10-Windows_8', 'SL-internet_explorer-9-Windows_7'], -# ['SL-internet_explorer-8-Windows_7', 'SL-internet_explorer-7-Windows_XP'] -# ] -# -# grunt.registerTask 'test:unit:sauce', 'Run tests with Karma on SauceLabs', ['dev', 'wait_for_sauce', 'saucetests:default'] -# -# grunt.registerTask 'test:func:sauce', 'Run functional tests on SauceLabs', ['dev', 'wait_for_sauce', 'mochaWebdriver:sauce'] -# -# grunt.registerMultiTask 'saucetests', 'Run Karma tests on sauce', -> -# self = @ -# done = @async() -# count = 0 -# results = [] -# success = true -# # we can only run 3 simultaneous browsers on SauceLabs -# grunt.util.async.forEachSeries @data.browsers, (browserSet, next) -> -# browsers = browserSet.join ',' -# port = util.sauceLabsAvailablePorts.pop() -# args = ["node_modules/karma/bin/karma", "start", self.data.configFile, -# "--browsers=#{browsers}", "--port=#{port}", "--single-run=true"] -# -# grunt.util.spawn -# cmd: 'node' -# grunt: false -# args: args -# opts: -# stdio: 'inherit' -# , (error, result, code) -> -# results[count] = code || 0 -# count++ -# if code != 0 -# success = false -# next() -# , (err) -> -# if err? -# done err -# else -# done success -# -# grunt.registerTask 'wait_for_sauce', '', -> -# done = @async() -# -# SauceLabs = require 'saucelabs' -# saucelabs = new SauceLabs -# username: process.env.SAUCE_USERNAME || process.env.SAUCE_USER_NAME -# password: process.env.SAUCE_ACCESSKEY || process.env.SAUCE_ACCESS_KEY -# -# jobs_in_progress = 1 -# -# # Filter out all the jobs that do NOT match `type` -# reject_jobs = (type, next) -> -# (jobs) -> -# jobs = jobs.filter (job) -> -# return job.status != type -# if typeof next == 'function' -# next jobs -# -# # Query SauceLabs' REST API for all jobs that have been run for this -# # account -# get_jobs = (next) -> -# saucelabs.getJobs (err, jobs) -> -# if err? -# grunt.log.error(error) -# if typeof next == 'function' -# next jobs -# -# # Wait until jobs_in_progress is 0, if it isn't keep querying -# # SauceLabs -# # Currently, if more than 0 jobs are running, this task will block -# # until 0 are running. This could be optimized since we can run -# # 3 at a time. -# wait = (next) -> -# if jobs_in_progress > 0 -# grunt.log.writeln("Waiting. #{jobs_in_progress} jobs running ...") -# in_progress = reject_jobs 'complete', (jobs, next) -> -# if jobs and jobs.length > 0 -# jobs_in_progress = jobs.length -# else -# jobs_in_progress = 0 -# get_jobs in_progress -# setTimeout wait, 10000, next -# else -# grunt.log.writeln("0 jobs running. Continuing forth!") -# if typeof next == 'function' -# next(true) -# wait done diff --git a/lib/grunt/utils.coffee b/lib/grunt/utils.coffee index 635dc4681..f861cb137 100644 --- a/lib/grunt/utils.coffee +++ b/lib/grunt/utils.coffee @@ -37,21 +37,6 @@ module.exports = grunt.fail.warn "Karma test(s) failed. Exit code: " + code done() - parallelTask: (args, options) -> - task = - grunt: true - args: args - stream: options && options.stream - - args.push '--port=' + @sauceLabsAvailablePorts.pop() - - if grunt.option 'reporters' - args.push '--reporters=' + grunt.option 'reporters' - - task - - sauceLabsAvailablePorts: [9000, 9001, 9080, 9090, 9876] - concat: (formulae) -> src = '' _.map(formulae, (f) -> diff --git a/lib/sauce/sauce_connect_block.sh b/lib/sauce/sauce_connect_block.sh deleted file mode 100755 index 126d37109..000000000 --- a/lib/sauce/sauce_connect_block.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - - -# Wait for Connect to be ready before exiting -while [ ! -f $SAUCE_CONNECT_READY_FILE ]; do - sleep .5 -done diff --git a/lib/sauce/sauce_connect_setup.sh b/lib/sauce/sauce_connect_setup.sh deleted file mode 100755 index 93dbc195c..000000000 --- a/lib/sauce/sauce_connect_setup.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash - -set -e - -# Setup and start Sauce Connect for your TravisCI build -# This script requires your .travis.yml to include the following two private env variables: -# SAUCE_USERNAME -# SAUCE_ACCESS_KEY -# Follow the steps at https://saucelabs.com/opensource/travis to set that up. -# -# Curl and run this script as part of your .travis.yml before_script section: -# before_script: -# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash - -CONNECT_URL="http://saucelabs.com/downloads/Sauce-Connect-latest.zip" -CONNECT_DIR="/tmp/sauce-connect-$RANDOM" -CONNECT_DOWNLOAD="Sauce_Connect.zip" - -if [[ -z "${LOGS_DIR}" ]]; then - LOGS_DIR="/tmp/fineuploader-build/logs" -fi -mkdir -p $LOGS_DIR - -CONNECT_LOG="$LOGS_DIR/sauce-connect.log" -CONNECT_STDOUT="$LOGS_DIR/sauce-connect.stdout" -CONNECT_STDERR="$LOGS_DIR/sauce-connect.stderr" -CONNECT_READYFILE="$LOGS_DIR/sauce-connect.ready-$RANDOM" -CONNECT_PID_FILE="$LOGS_DIR/sauce-connect.pid" - -# Get Connect and start it -mkdir -p $CONNECT_DIR -cd $CONNECT_DIR -echo "Downloading Sauce-Connect" -curl -# $CONNECT_URL -o $CONNECT_DOWNLOAD -echo "got it!" -unzip $CONNECT_DOWNLOAD > /dev/null -rm $CONNECT_DOWNLOAD - - - -ARGS="" - -# Set tunnel-id only on Travis, to make local testing easier. -if [ ! -z "$TRAVIS_JOB_NUMBER" ]; then - #ARGS="$ARGS --tunnel-identifier $TRAVIS_JOB_NUMBER" - echo $TRAVIS_JOB_NUMBER -fi -if [ ! -z "$SAUCE_CONNECT_READY_FILE" ]; then - ARGS="$ARGS --readyfile $SAUCE_CONNECT_READY_FILE" -fi - - -echo "Starting Sauce Connect in the background, logging into:" -echo " $CONNECT_LOG" -echo " $CONNECT_STDOUT" -echo " $CONNECT_STDERR" -echo " $CONNCT_PID" - - -#2> $CONNECT_STDERR 1> $CONNECT_STDOUT & - -if [[ ! -z "$SAUCE_USERNAME" && ! -z "$SAUCE_ACCESS_KEY" ]]; then - java -jar Sauce-Connect.jar \ - $SAUCE_USERNAME $SAUCE_ACCESS_KEY \ - $ARGS \ - --logfile $CONNECT_LOG \ - --readyfile $CONNECT_READYFILE 2> $CONNECT_STDERR 1> $CONNECT_STDOUT & - - #CONNECT_PID=$! - #touch $CONNECT_PID_FILE - #echo $CONNECT_PID > $CONNECT_PID_FILE -else - echo "Requires SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables" -fi - - diff --git a/test/README.md b/test/README.md deleted file mode 100644 index e9b78c1e0..000000000 --- a/test/README.md +++ /dev/null @@ -1,45 +0,0 @@ -## FineUploader test suite - -### What's in here? - -##### Unit Tests - -Unit tests are found in the `./unit/` directory here. They contain javascript files whose purpoose is to test specific, atomic units of the codebase. They are not (yet) comprehensive, but cover a lot of bases. - -Before running tests you'll need to prepare the codebase by running: - -`% grunt dev` - -This will download test dependencies with bower and npm, concatenate the source -files, and copy everything into its proper area for testing. - -You can run the unit tests on your local machine by issuing: - -`% grunt test:unit` - -You can pass in normal [Karma](http://karma-runner.github.io/0.10/index.html) -command line arguments: - -`% grunt test:unit --browsers PhantomJS,Chrome,Firefox` - -You can have saucelabs run the unit tests on all sorts of browsers with: - -``` -% export SAUCE_USERNAME='your saucelabs username' -% export SAUCE_ACCESSKEY='your saucelabs accesskey' -% grunt test:unit:sauce -``` - -_Note that these test can take 10+ minutes to run_ - -You have have grunt automatically re-build and re-run tests for you as changes are made with: - -``` -% grunt autotest -``` - -Other grunt commands are documented and can be viewed with: - -``` -% grunt --help -``` diff --git a/test/functional/spike.coffee b/test/functional/spike.coffee deleted file mode 100644 index 25109120a..000000000 --- a/test/functional/spike.coffee +++ /dev/null @@ -1,31 +0,0 @@ -assert = require 'assert' -async = require 'async' - -describe "Mocha test run by grunt-mocha-sauce", -> - - it 'has a browser injected into it', -> - assert.ok @browser - -describe "A basic Webdriver example", -> - describe "injected browser executing a Google Search", -> - it "performs as expected", (done) -> - searchBox = undefined - browser = @browser - async.waterfall [(cb) -> - browser.get "http://google.com", cb - , (cb) -> - browser.elementByName "q", cb - , (el, cb) -> - searchBox = el - searchBox.type "webdriver", cb - , (cb) -> - searchBox.getAttribute "value", cb - , (val, cb) -> - try - assert.equal val, "webdriver" - cb() - catch e - cb e - ], done - - From 745e2857c80221bac5e45b2fe69a36cdc64bcb3d Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 20:50:57 -0500 Subject: [PATCH 052/137] chore(package.json): #1t st112 - Move all dependencies in to "dependencies" Just a better idea unless Fine Uploader is one day put on npm in which case all these dependencies would be put into devDependencies --- Gruntfile.coffee | 2 +- package.json | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 7b4b0b64f..d12ca0350 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -93,7 +93,7 @@ module.exports = (grunt) -> # Dependencies # ========== - for name of pkg.devDependencies when name.substring(0, 6) is 'grunt-' + for name of pkg.dependencies when name.substring(0, 6) is 'grunt-' grunt.loadNpmTasks name grunt.loadTasks './lib/grunt' diff --git a/package.json b/package.json index 9e6bc4fc8..9c60c282e 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,13 @@ "bugs": { "url": "https://github.com/Widen/fine-uploader/issues" }, - "devDependencies": { + "devDependencies": {}, + "dependencies": { "async": "~0.2.9", + "bower": "1.2.5", + "coffee-script": "~1.6.3", + "glob": "~3.2.6", + "grunt": "~0.4.1", "grunt-banner": "~0.2.0", "grunt-bower-task": "0.3.2", "grunt-contrib-clean": "~0.4.1", @@ -54,23 +59,17 @@ "grunt-contrib-uglify": "~0.2.2", "grunt-contrib-watch": "~0.4.4", "grunt-karma": "0.6.2", + "grunt-nodestatic": "0.1.2", "grunt-open": "~0.2.2", "grunt-shell": "~0.3.1", "grunt-strip-code": "~0.1.2", "grunt-version": "~0.2.2", + "karma": "0.10.10", + "karma-firefox-launcher": "0.1.2", + "karma-ievms": "0.0.4", "karma-mocha": "0.1.0", "karma-opera-launcher": "0.1.0", "karma-safari-launcher": "0.1.1", - "karma-ievms": "0.0.4", - "grunt-nodestatic": "0.1.2", - "karma-firefox-launcher": "0.1.2" - }, - "dependencies": { - "bower": "1.2.5", - "coffee-script": "~1.6.3", - "glob": "~3.2.6", - "grunt": "~0.4.1", - "karma": "0.10.10", "optimist": "~0.6.0", "request": "~2.21.0", "semver": "2.0.x", From 3c9273cae7ee7a7a014f29fed8328acacd565fe9 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 20:53:49 -0500 Subject: [PATCH 053/137] chore($grunt/tasks): #1112 - Add custom tasks This needs to eventually be refactor into separate tasks (I think), but leaving here so tests do not fail. --- lib/grunt/custom.coffee | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 lib/grunt/custom.coffee diff --git a/lib/grunt/custom.coffee b/lib/grunt/custom.coffee new file mode 100644 index 000000000..241c3e0ce --- /dev/null +++ b/lib/grunt/custom.coffee @@ -0,0 +1,21 @@ +util = require './utils' +path = require 'path' +spawn = require('child_process').spawn +sourceModules = require('../modules') + +module.exports = (grunt) -> + + _ = grunt.util._ + grunt.registerTask 'lint', 'Lint, in order, the Gruntfile, sources, and tests.', ['concurrent:lint'] + + grunt.registerTask 'minify', 'Minify the source javascript and css', [ + 'cssmin:all', 'uglify:core', 'uglify:jquery', 'uglify:coreS3', + 'uglify:jqueryS3', 'uglify:jqueryAzure', 'uglify:coreAzure', 'uglify:all'] + + grunt.registerMultiTask 'tests', '** Use ` grunt-test` instead **', -> + util.startKarma.call util, @data, @async() + + grunt.registerTask 'check_pull_req', '', -> + util.checkPullRequest() + + From 1bcaaa4f12fb531e054e9a22eeedff4b6a8bca02 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 21:28:52 -0500 Subject: [PATCH 054/137] chore(Gruntfile): #1112 - Move custom task functions into Gruntfile --- Gruntfile.coffee | 24 ++++++++++++++++++------ lib/grunt/custom.coffee | 21 --------------------- 2 files changed, 18 insertions(+), 27 deletions(-) delete mode 100644 lib/grunt/custom.coffee diff --git a/Gruntfile.coffee b/Gruntfile.coffee index d12ca0350..7dd09cd7b 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -6,13 +6,16 @@ ### -tasks = require('./lib/grunt/tasks') module.exports = (grunt) -> fs = require 'fs' uuid = require 'uuid' async = require 'async' + path = require 'path' + spawn = require('child_process').spawn + utils = require './lib/grunt/utils' + tasks = require('./lib/grunt/tasks') # Utilities # ========== @@ -96,8 +99,6 @@ module.exports = (grunt) -> for name of pkg.dependencies when name.substring(0, 6) is 'grunt-' grunt.loadNpmTasks name - grunt.loadTasks './lib/grunt' - grunt.registerTask 'build_details', -> grunt.log.writeln "\n##########" grunt.log.writeln "Custom Build Generated: " @@ -194,6 +195,18 @@ module.exports = (grunt) -> grunt.task.run(taskList) + grunt.registerTask 'lint', 'Lint, in order, the Gruntfile, sources, and tests.', ['concurrent:lint'] + + grunt.registerTask 'minify', 'Minify the source javascript and css', [ + 'cssmin:all', 'uglify:core', 'uglify:jquery', 'uglify:coreS3', + 'uglify:jqueryS3', 'uglify:jqueryAzure', 'uglify:coreAzure', 'uglify:all'] + + grunt.registerMultiTask 'tests', '** Use ` grunt-test` instead **', -> + utils.startKarma.call utils, @data, @async() + + grunt.registerTask 'check_pull_req', '', -> + utils.checkPullRequest() + grunt.registerTask 'travis', 'Test with Travis CI', ['check_pull_req', 'dev', 'test:travis'] grunt.registerTask 'dev', 'Prepare code for testing', ['clean', 'bower', 'build', 'copy:test'] @@ -204,12 +217,11 @@ module.exports = (grunt) -> grunt.registerTask 'package', 'Build a zipped distribution-worthy version', ['build_stripped', 'copy:dist', 'shell:version_dist_templates', 'compress:jquery', 'compress:jqueryS3', 'compress:jqueryAzure', 'compress:core', 'compress:coreS3', 'compress:coreAzure' ] grunt.registerTask 'custom', 'Build a custom version', (modules) -> - util = require './lib/grunt/utils' dest = customBuildDest if (modules?) - util.build.call util, dest, modules.split(',') + utils.build.call utils, dest, modules.split(',') else - util.build.call util, dest, [] + utils.build.call utils, dest, [] grunt.task.run(['uglify:custom', 'cssmin:custom', 'strip_code:custom', 'shell:version_custom_templates', 'usebanner:customhead', 'usebanner:customfoot', 'compress:custom', 'build_details']) grunt.registerTask 'default', 'Default task: clean, bower, lint, build, & test', ['package'] diff --git a/lib/grunt/custom.coffee b/lib/grunt/custom.coffee deleted file mode 100644 index 241c3e0ce..000000000 --- a/lib/grunt/custom.coffee +++ /dev/null @@ -1,21 +0,0 @@ -util = require './utils' -path = require 'path' -spawn = require('child_process').spawn -sourceModules = require('../modules') - -module.exports = (grunt) -> - - _ = grunt.util._ - grunt.registerTask 'lint', 'Lint, in order, the Gruntfile, sources, and tests.', ['concurrent:lint'] - - grunt.registerTask 'minify', 'Minify the source javascript and css', [ - 'cssmin:all', 'uglify:core', 'uglify:jquery', 'uglify:coreS3', - 'uglify:jqueryS3', 'uglify:jqueryAzure', 'uglify:coreAzure', 'uglify:all'] - - grunt.registerMultiTask 'tests', '** Use ` grunt-test` instead **', -> - util.startKarma.call util, @data, @async() - - grunt.registerTask 'check_pull_req', '', -> - util.checkPullRequest() - - From aa1e3148f0d50e8a377dae4024d8a395727b971e Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 21:38:52 -0500 Subject: [PATCH 055/137] test: #1112 - Run travis with debug and verbose so I can figure out why tests are failing --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 56e4f080f..c7150efa1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ before_script: - "sh -e /etc/init.d/xvfb start" script: -- grunt travis + - grunt travis --verbose --debug branches: only: From 890e0eb39e65ba378282e1d5363df7dcf6d25008 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 21:54:36 -0500 Subject: [PATCH 056/137] test: #1112 - Attempt to not use path.join --- lib/grunt/tasks/bower.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/grunt/tasks/bower.js b/lib/grunt/tasks/bower.js index 63e03e194..575518217 100644 --- a/lib/grunt/tasks/bower.js +++ b/lib/grunt/tasks/bower.js @@ -7,7 +7,7 @@ module.exports = function(paths) { return { "test": { options: { - targetDir: path.join(paths.test, "_vendor"), + targetDir: paths.test+"/_vendor", install: true, cleanTargetDir: true, cleanBowerDir: true, From 14019e08b707ba60a57b4d7ba18c11208c379bea Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Fri, 11 Jul 2014 21:55:21 -0500 Subject: [PATCH 057/137] chore(package.json): #1112 - Remove "reasonably close" version specifier --- package.json | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 9c60c282e..7fef1a0ac 100644 --- a/package.json +++ b/package.json @@ -43,38 +43,38 @@ }, "devDependencies": {}, "dependencies": { - "async": "~0.2.9", + "async": "0.2.9", "bower": "1.2.5", - "coffee-script": "~1.6.3", - "glob": "~3.2.6", - "grunt": "~0.4.1", - "grunt-banner": "~0.2.0", + "coffee-script": "1.6.3", + "glob": "3.2.6", + "grunt": "0.4.1", + "grunt-banner": "0.2.0", "grunt-bower-task": "0.3.2", - "grunt-contrib-clean": "~0.4.1", - "grunt-contrib-compress": "~0.5.2", - "grunt-contrib-concat": "~0.3.0", - "grunt-contrib-copy": "~0.4.1", - "grunt-contrib-cssmin": "~0.6.1", - "grunt-contrib-jshint": "~0.7.1", - "grunt-contrib-uglify": "~0.2.2", - "grunt-contrib-watch": "~0.4.4", + "grunt-contrib-clean": "0.4.1", + "grunt-contrib-compress": "0.5.2", + "grunt-contrib-concat": "0.3.0", + "grunt-contrib-copy": "0.4.1", + "grunt-contrib-cssmin": "0.6.1", + "grunt-contrib-jshint": "0.7.1", + "grunt-contrib-uglify": "0.2.2", + "grunt-contrib-watch": "0.4.4", "grunt-karma": "0.6.2", "grunt-nodestatic": "0.1.2", - "grunt-open": "~0.2.2", - "grunt-shell": "~0.3.1", - "grunt-strip-code": "~0.1.2", - "grunt-version": "~0.2.2", + "grunt-open": "0.2.2", + "grunt-shell": "0.3.1", + "grunt-strip-code": "0.1.2", + "grunt-version": "0.2.2", "karma": "0.10.10", "karma-firefox-launcher": "0.1.2", "karma-ievms": "0.0.4", "karma-mocha": "0.1.0", "karma-opera-launcher": "0.1.0", "karma-safari-launcher": "0.1.1", - "optimist": "~0.6.0", - "request": "~2.21.0", + "optimist": "0.6.0", + "request": "2.21.0", "semver": "2.0.x", - "underscore": "~1.5.2", - "uuid": "~1.4.1" + "underscore": "1.5.2", + "uuid": "1.4.1" }, "license": "GPLv3" } From 37e2b62cb01853d93ecdbe2647f44f5f1e1d9765 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 13:25:17 -0500 Subject: [PATCH 058/137] revert previous two commits --- .travis.yml | 2 +- lib/grunt/tasks/bower.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c7150efa1..56e4f080f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ before_script: - "sh -e /etc/init.d/xvfb start" script: - - grunt travis --verbose --debug +- grunt travis branches: only: diff --git a/lib/grunt/tasks/bower.js b/lib/grunt/tasks/bower.js index 575518217..63e03e194 100644 --- a/lib/grunt/tasks/bower.js +++ b/lib/grunt/tasks/bower.js @@ -7,7 +7,7 @@ module.exports = function(paths) { return { "test": { options: { - targetDir: paths.test+"/_vendor", + targetDir: path.join(paths.test, "_vendor"), install: true, cleanTargetDir: true, cleanBowerDir: true, From f1051a9667aa324426881a824ac4544bd166a722 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 13:26:04 -0500 Subject: [PATCH 059/137] chore(package.json): Add updated grunt-bower-task dep from pull request It uses bower ~1.3.0 instead of ~1.2.0 which fixes bower installs --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7fef1a0ac..4f503865b 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "glob": "3.2.6", "grunt": "0.4.1", "grunt-banner": "0.2.0", - "grunt-bower-task": "0.3.2", + "grunt-bower-task": "git+https://github.com/pogoseat/grunt-bower-task.git", "grunt-contrib-clean": "0.4.1", "grunt-contrib-compress": "0.5.2", "grunt-contrib-concat": "0.3.0", From 04e8b58d31a708b4627b7458f8811d97d5df0c8b Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 14:32:16 -0500 Subject: [PATCH 060/137] chore($grunt/tasks): #1112 - s/tasks/configs/g for actualy grunt configs --- Gruntfile.coffee | 28 ++++++++++++----------- lib/grunt/{tasks => configs}/banner.js | 0 lib/grunt/{tasks => configs}/bower.js | 0 lib/grunt/{tasks => configs}/clean.js | 0 lib/grunt/{tasks => configs}/compress.js | 0 lib/grunt/{tasks => configs}/concat.js | 0 lib/grunt/{tasks => configs}/copy.js | 0 lib/grunt/{tasks => configs}/cssmin.js | 0 lib/grunt/configs/index.js | 17 ++++++++++++++ lib/grunt/{tasks => configs}/jshint.js | 0 lib/grunt/{tasks => configs}/static.js | 0 lib/grunt/{tasks => configs}/stripcode.js | 0 lib/grunt/{tasks => configs}/uglify.js | 0 lib/grunt/{tasks => configs}/version.js | 0 lib/grunt/{tasks => configs}/watch.js | 0 lib/grunt/index.js | 8 +++++++ lib/grunt/tasks/index.js | 13 ----------- 17 files changed, 40 insertions(+), 26 deletions(-) rename lib/grunt/{tasks => configs}/banner.js (100%) rename lib/grunt/{tasks => configs}/bower.js (100%) rename lib/grunt/{tasks => configs}/clean.js (100%) rename lib/grunt/{tasks => configs}/compress.js (100%) rename lib/grunt/{tasks => configs}/concat.js (100%) rename lib/grunt/{tasks => configs}/copy.js (100%) rename lib/grunt/{tasks => configs}/cssmin.js (100%) create mode 100644 lib/grunt/configs/index.js rename lib/grunt/{tasks => configs}/jshint.js (100%) rename lib/grunt/{tasks => configs}/static.js (100%) rename lib/grunt/{tasks => configs}/stripcode.js (100%) rename lib/grunt/{tasks => configs}/uglify.js (100%) rename lib/grunt/{tasks => configs}/version.js (100%) rename lib/grunt/{tasks => configs}/watch.js (100%) create mode 100644 lib/grunt/index.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 7dd09cd7b..75ceed0ec 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -15,7 +15,9 @@ module.exports = (grunt) -> path = require 'path' spawn = require('child_process').spawn utils = require './lib/grunt/utils' + tasks = require('./lib/grunt/tasks') + configs = require('./lib/grunt/configs') # Utilities # ========== @@ -58,19 +60,19 @@ module.exports = (grunt) -> pkg: pkg - bower: tasks.bower(paths) - clean: tasks.clean(paths) - compress: tasks.compress(paths, customBuildDest) - concat: tasks.concat(paths) - copy: tasks.copy(paths) - cssmin: tasks.cssmin(paths, customBuildDest) - jshint: tasks.jshint(paths) - nodestatic: tasks.static(paths) - strip_code: tasks.stripcode(paths, customBuildDest) - uglify: tasks.uglify(paths, customBuildDest) - usebanner: tasks.banner(paths, customBuildDest) - version: tasks.version(pkg) - watch: tasks.watch(paths) + bower: configs.bower(paths) + clean: configs.clean(paths) + compress: configs.compress(paths, customBuildDest) + concat: configs.concat(paths) + copy: configs.copy(paths) + cssmin: configs.cssmin(paths, customBuildDest) + jshint: configs.jshint(paths) + nodestatic: configs.static(paths) + strip_code: configs.stripcode(paths, customBuildDest) + uglify: configs.uglify(paths, customBuildDest) + usebanner: configs.banner(paths, customBuildDest) + version: configs.version(pkg) + watch: configs.watch(paths) custom: options: diff --git a/lib/grunt/tasks/banner.js b/lib/grunt/configs/banner.js similarity index 100% rename from lib/grunt/tasks/banner.js rename to lib/grunt/configs/banner.js diff --git a/lib/grunt/tasks/bower.js b/lib/grunt/configs/bower.js similarity index 100% rename from lib/grunt/tasks/bower.js rename to lib/grunt/configs/bower.js diff --git a/lib/grunt/tasks/clean.js b/lib/grunt/configs/clean.js similarity index 100% rename from lib/grunt/tasks/clean.js rename to lib/grunt/configs/clean.js diff --git a/lib/grunt/tasks/compress.js b/lib/grunt/configs/compress.js similarity index 100% rename from lib/grunt/tasks/compress.js rename to lib/grunt/configs/compress.js diff --git a/lib/grunt/tasks/concat.js b/lib/grunt/configs/concat.js similarity index 100% rename from lib/grunt/tasks/concat.js rename to lib/grunt/configs/concat.js diff --git a/lib/grunt/tasks/copy.js b/lib/grunt/configs/copy.js similarity index 100% rename from lib/grunt/tasks/copy.js rename to lib/grunt/configs/copy.js diff --git a/lib/grunt/tasks/cssmin.js b/lib/grunt/configs/cssmin.js similarity index 100% rename from lib/grunt/tasks/cssmin.js rename to lib/grunt/configs/cssmin.js diff --git a/lib/grunt/configs/index.js b/lib/grunt/configs/index.js new file mode 100644 index 000000000..b01659fd9 --- /dev/null +++ b/lib/grunt/configs/index.js @@ -0,0 +1,17 @@ +/* jshint node: true */ +module.exports = { + banner: require("./banner"), + bower: require("./bower"), + clean: require("./clean"), + compress: require("./compress"), + concat: require("./concat"), + copy: require("./copy"), + cssmin: require("./cssmin"), + jshint: require("./jshint"), + static: require("./static"), + stripcode: require("./stripcode"), + uglify: require("./uglify"), + version: require("./version"), + watch: require("./watch"), +}; + diff --git a/lib/grunt/tasks/jshint.js b/lib/grunt/configs/jshint.js similarity index 100% rename from lib/grunt/tasks/jshint.js rename to lib/grunt/configs/jshint.js diff --git a/lib/grunt/tasks/static.js b/lib/grunt/configs/static.js similarity index 100% rename from lib/grunt/tasks/static.js rename to lib/grunt/configs/static.js diff --git a/lib/grunt/tasks/stripcode.js b/lib/grunt/configs/stripcode.js similarity index 100% rename from lib/grunt/tasks/stripcode.js rename to lib/grunt/configs/stripcode.js diff --git a/lib/grunt/tasks/uglify.js b/lib/grunt/configs/uglify.js similarity index 100% rename from lib/grunt/tasks/uglify.js rename to lib/grunt/configs/uglify.js diff --git a/lib/grunt/tasks/version.js b/lib/grunt/configs/version.js similarity index 100% rename from lib/grunt/tasks/version.js rename to lib/grunt/configs/version.js diff --git a/lib/grunt/tasks/watch.js b/lib/grunt/configs/watch.js similarity index 100% rename from lib/grunt/tasks/watch.js rename to lib/grunt/configs/watch.js diff --git a/lib/grunt/index.js b/lib/grunt/index.js new file mode 100644 index 000000000..fb1a25b82 --- /dev/null +++ b/lib/grunt/index.js @@ -0,0 +1,8 @@ +/* jshint node: true */ +var configs = require("./configs"); +var tasks = require("./tasks"); + +module.exports = { + configs: configs, + tasks: tasks +}; diff --git a/lib/grunt/tasks/index.js b/lib/grunt/tasks/index.js index b01659fd9..be425a6f3 100644 --- a/lib/grunt/tasks/index.js +++ b/lib/grunt/tasks/index.js @@ -1,17 +1,4 @@ /* jshint node: true */ module.exports = { - banner: require("./banner"), - bower: require("./bower"), - clean: require("./clean"), - compress: require("./compress"), - concat: require("./concat"), - copy: require("./copy"), - cssmin: require("./cssmin"), - jshint: require("./jshint"), - static: require("./static"), - stripcode: require("./stripcode"), - uglify: require("./uglify"), - version: require("./version"), - watch: require("./watch"), }; From 30530b118a73b67364c81797a9100c9aedb13579 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 14:51:10 -0500 Subject: [PATCH 061/137] chore($grunt/tasks): #1112 - Move test task to own module --- Gruntfile.coffee | 92 ++------------------------------------ lib/grunt/tasks/test.js | 98 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 89 deletions(-) create mode 100644 lib/grunt/tasks/test.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 75ceed0ec..377c7d2b5 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -16,8 +16,8 @@ module.exports = (grunt) -> spawn = require('child_process').spawn utils = require './lib/grunt/utils' - tasks = require('./lib/grunt/tasks') configs = require('./lib/grunt/configs') + tasks = './lib/grunt/tasks' # Utilities # ========== @@ -107,96 +107,10 @@ module.exports = (grunt) -> grunt.log.write "### " + customBuildDest + " ###" grunt.log.writeln "\n##########\n" + grunt.loadTasks(tasks) + # Tasks # ========== - grunt.registerTask 'test', "Run unit tests. Allows: 'travis', 'server', 'headless', 'ie', and 'all'. Can also take browser names: 'PhantomJS', 'Firefox', 'Chrome', 'Safari', etc.. Comma-delimited.", (test_type) -> - # To run this task: - # % grunt test: - # - # Where is either: - # * 'travis', 'server', 'headless', 'ie', 'ios', or 'all' - # * a comma-delimited list of browsers. - # - # Example: - # % grunt test:server - # % grunt test:headless - # % grunt test:PhantomJS --no-single-run - # % grunt test:Firefox,Chrome,Opera,Safari - # % grunt test:ie - # % grunt test:Firefox,Chrome,Opera,Safari --autoWatch=true --singleRun=true - # etc... - taskList = ["server"] - - setDefaultOption = (name, def) -> - if not grunt.option(name)? - grunt.option(name, def) - - switch test_type - when "travis" then do -> - setDefaultOption('singleRun', true) - setDefaultOption('autoWatch', true) - taskList.push('tests:travis') - when "server" then do -> - setDefaultOption('singleRun', false) - setDefaultOption('autoWatch', false) - grunt.option('browsers', []) - taskList.push('tests:local') - when "headless" then do -> - setDefaultOption('singleRun', true) - setDefaultOption('autoWatch', true) - #grunt.option('autoWatch') || true - #grunt.option('singleRun') || true - grunt.option('browsers', ['PhantomJS']) - taskList.push('tests:local') - when "ie" then do -> - setDefaultOption('singleRun', true) - setDefaultOption('autoWatch', true) - #grunt.option('autoWatch') || true - #grunt.option('singleRun') || true - taskList.push('tests:local') - grunt.option('browsers', [ - 'IE7 - WinXP', - 'IE8 - WinXP', - 'IE9 - Win7', - 'IE10 - Win7', - 'IE11 - Win7' - ]) - when "ios" then do -> - setDefaultOption('singleRun', true) - setDefaultOption('autoWatch', true) - grunt.option('browsers', ['iOS']) - taskList.push('tests:local') - when "all" then do -> - setDefaultOption('singleRun', true) - setDefaultOption('autoWatch', true) - grunt.option('browsers', [ - 'PhantomJS', - 'Firefox', - 'Chrome', - 'Safari', - 'Opera', - 'IE7 - WinXP', - 'IE8 - WinXP', - 'IE9 - Win7', - 'IE10 - Win7', - 'IE11 - Win7' - ]) - taskList.push('tests:local') - else do -> - if (test_type?) - setDefaultOption('singleRun', true) - setDefaultOption('autoWatch', true) - if (',' in test_type) - tests = test_type.split(',') - grunt.option('browsers', tests) - else - grunt.option('browsers', [test_type]) - else - grunt.option('browsers') || ['Chrome'] - taskList.push('tests:local') - - grunt.task.run(taskList) - grunt.registerTask 'lint', 'Lint, in order, the Gruntfile, sources, and tests.', ['concurrent:lint'] grunt.registerTask 'minify', 'Minify the source javascript and css', [ diff --git a/lib/grunt/tasks/test.js b/lib/grunt/tasks/test.js new file mode 100644 index 000000000..3fb3cf7c7 --- /dev/null +++ b/lib/grunt/tasks/test.js @@ -0,0 +1,98 @@ +/* jshint node: true */ +// To run this task: +// % grunt test: +// +// Where is either: +// * 'travis', 'server', 'headless', 'ie', 'ios', or 'all' +// * a comma-delimited list of browsers. +// +// Example: +// % grunt test:server +// % grunt test:headless +// % grunt test:PhantomJS --no-single-run +// % grunt test:Firefox,Chrome,Opera,Safari +// % grunt test:ie +// % grunt test:Firefox,Chrome,Opera,Safari --autoWatch=true --singleRun=true +// etc... + +module.exports = function(grunt){ + "use strict"; + + grunt.registerTask("test", "Run unit tests. Allows: 'travis', 'server', 'headless', 'ie', and 'all'. Can also take browser names: 'PhantomJS', 'Firefox', 'Chrome', 'Safari', etc.. Comma-delimited.", function(test_type) { + var setDefaultOption, taskList; + taskList = ["server"]; + setDefaultOption = function(name, def) { + if (grunt.option(name) == null) { + return grunt.option(name, def); + } + }; + switch (test_type) { + case "travis": + (function() { + setDefaultOption("singleRun", true); + setDefaultOption("autoWatch", true); + return taskList.push("tests:travis"); + })(); + break; + case "server": + (function() { + setDefaultOption("singleRun", false); + setDefaultOption("autoWatch", false); + grunt.option("browsers", []); + return taskList.push("tests:local"); + })(); + break; + case "headless": + (function() { + setDefaultOption("singleRun", true); + setDefaultOption("autoWatch", true); + grunt.option("browsers", ["PhantomJS"]); + return taskList.push("tests:local"); + })(); + break; + case "ie": + (function() { + setDefaultOption("singleRun", true); + setDefaultOption("autoWatch", true); + taskList.push("tests:local"); + return grunt.option("browsers", ["IE7 - WinXP", "IE8 - WinXP", "IE9 - Win7", "IE10 - Win7", "IE11 - Win7"]); + })(); + break; + case "ios": + (function() { + setDefaultOption("singleRun", true); + setDefaultOption("autoWatch", true); + grunt.option("browsers", ["iOS"]); + return taskList.push("tests:local"); + })(); + break; + case "all": + (function() { + setDefaultOption("singleRun", true); + setDefaultOption("autoWatch", true); + grunt.option("browsers", ["PhantomJS", "Firefox", "Chrome", "Safari", "Opera", "IE7 - WinXP", "IE8 - WinXP", "IE9 - Win7", "IE10 - Win7", "IE11 - Win7"]); + return taskList.push("tests:local"); + })(); + break; + default: + (function() { + var tests; + if ((test_type != null)) { + setDefaultOption("singleRun", true); + setDefaultOption("autoWatch", true); + if (tests.indexOf(test_type, ",") >= 0) { + tests = test_type.split(","); + grunt.option("browsers", tests); + } else { + grunt.option("browsers", [test_type]); + } + } else { + grunt.option("browsers") || ["Chrome"]; + } + return taskList.push("tests:local"); + })(); + } + return grunt.task.run(taskList); + }); + +}; From fcb33b9a925fe87084e6d991e76f03203c255143 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 14:51:39 -0500 Subject: [PATCH 062/137] chore($grunt/tasks): #1112 - Move minify task to own module --- Gruntfile.coffee | 6 ------ lib/grunt/tasks/minify.js | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 lib/grunt/tasks/minify.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 377c7d2b5..ad9e7b5fa 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -111,12 +111,6 @@ module.exports = (grunt) -> # Tasks # ========== - grunt.registerTask 'lint', 'Lint, in order, the Gruntfile, sources, and tests.', ['concurrent:lint'] - - grunt.registerTask 'minify', 'Minify the source javascript and css', [ - 'cssmin:all', 'uglify:core', 'uglify:jquery', 'uglify:coreS3', - 'uglify:jqueryS3', 'uglify:jqueryAzure', 'uglify:coreAzure', 'uglify:all'] - grunt.registerMultiTask 'tests', '** Use ` grunt-test` instead **', -> utils.startKarma.call utils, @data, @async() diff --git a/lib/grunt/tasks/minify.js b/lib/grunt/tasks/minify.js new file mode 100644 index 000000000..20af10e37 --- /dev/null +++ b/lib/grunt/tasks/minify.js @@ -0,0 +1,19 @@ +/* jshint node: true */ + +module.exports = function(grunt){ + "use strict"; + + grunt.registerTask( + "minify", + "Minify the source javascript and css", + [ + "cssmin:all", + "uglify:core", + "uglify:jquery", + "uglify:coreS3", + "uglify:jqueryS3", + "uglify:jqueryAzure", + "uglify:coreAzure", + "uglify:all", + ]); +}; From 535c8d4751d07d97adbfbb16e30f908a97c7de5a Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 15:05:31 -0500 Subject: [PATCH 063/137] chore($grunt/tasks): #1112 - Move tests task to own module --- Gruntfile.coffee | 3 --- lib/grunt/tasks/tests.js | 12 ++++++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 lib/grunt/tasks/tests.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index ad9e7b5fa..e74e8f307 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -111,9 +111,6 @@ module.exports = (grunt) -> # Tasks # ========== - grunt.registerMultiTask 'tests', '** Use ` grunt-test` instead **', -> - utils.startKarma.call utils, @data, @async() - grunt.registerTask 'check_pull_req', '', -> utils.checkPullRequest() diff --git a/lib/grunt/tasks/tests.js b/lib/grunt/tasks/tests.js new file mode 100644 index 000000000..e61e9d607 --- /dev/null +++ b/lib/grunt/tasks/tests.js @@ -0,0 +1,12 @@ +/* jshint node: true */ +var utils = require('../utils'); + + +module.exports = function(grunt){ + "use strict"; + + grunt.registerMultiTask("tests", "** Use ` grunt-test` instead **", function() { + return utils.startKarma.call(utils, this.data, this.async()); + }); + +}; From 23da4073bcca2bf4508fe7243c2a2030fd0a093f Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 20:50:38 -0500 Subject: [PATCH 064/137] chore($grunt/tasks): #1112 - Move validate_pull_request task to own module --- Gruntfile.coffee | 5 +---- lib/grunt/tasks/validate_pull_request.js | 11 +++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 lib/grunt/tasks/validate_pull_request.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index e74e8f307..ca30c869d 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -111,10 +111,7 @@ module.exports = (grunt) -> # Tasks # ========== - grunt.registerTask 'check_pull_req', '', -> - utils.checkPullRequest() - - grunt.registerTask 'travis', 'Test with Travis CI', ['check_pull_req', 'dev', 'test:travis'] + grunt.registerTask 'travis', 'Test with Travis CI', ['validate_pull_request', 'dev', 'test:travis'] grunt.registerTask 'dev', 'Prepare code for testing', ['clean', 'bower', 'build', 'copy:test'] diff --git a/lib/grunt/tasks/validate_pull_request.js b/lib/grunt/tasks/validate_pull_request.js new file mode 100644 index 000000000..4fbe5d46d --- /dev/null +++ b/lib/grunt/tasks/validate_pull_request.js @@ -0,0 +1,11 @@ +/* jshint node: true */ +var utils = require("../utils"); + +module.exports = function(grunt){ + "use strict"; + + grunt.registerTask("validate_pull_request", "", function() { + return utils.checkPullRequest(); + }); + +}; From 7ca3f2d73b8d074042a16206dbb614a90b375ae4 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 21:10:31 -0500 Subject: [PATCH 065/137] chore($grunt/tasks): #1112 - Move travis task to own module --- Gruntfile.coffee | 1 - lib/grunt/tasks/travis.js | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 lib/grunt/tasks/travis.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index ca30c869d..35760f7b4 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -111,7 +111,6 @@ module.exports = (grunt) -> # Tasks # ========== - grunt.registerTask 'travis', 'Test with Travis CI', ['validate_pull_request', 'dev', 'test:travis'] grunt.registerTask 'dev', 'Prepare code for testing', ['clean', 'bower', 'build', 'copy:test'] diff --git a/lib/grunt/tasks/travis.js b/lib/grunt/tasks/travis.js new file mode 100644 index 000000000..9bc570dba --- /dev/null +++ b/lib/grunt/tasks/travis.js @@ -0,0 +1,15 @@ +/* jshint node: true */ + +module.exports = function(grunt){ + "use strict"; + + grunt.registerTask( + "travis", + "Test with Travis CI", + [ + "validate_pull_request", + "dev", + "test:travis" + ]); + +}; From be3836581e6a1319266f36f19c6a7ed6f1aa26b2 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 21:11:02 -0500 Subject: [PATCH 066/137] chore($grunt/tasks): #1112 - Move dev task to own module --- Gruntfile.coffee | 3 --- lib/grunt/tasks/dev.js | 9 +++++++++ 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 lib/grunt/tasks/dev.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 35760f7b4..0c196eaed 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -111,9 +111,6 @@ module.exports = (grunt) -> # Tasks # ========== - - grunt.registerTask 'dev', 'Prepare code for testing', ['clean', 'bower', 'build', 'copy:test'] - grunt.registerTask 'build', 'Build from latest source', ['jshint:source', 'jshint:tests', 'concat', 'minify', 'usebanner:allhead', 'usebanner:allfoot', 'copy:images'] grunt.registerTask 'build_stripped', 'Build from latest source w/ test artifacts stripped out', ['concat', 'strip_code:build', 'minify', 'usebanner:allhead', 'usebanner:allfoot', 'copy:images'] diff --git a/lib/grunt/tasks/dev.js b/lib/grunt/tasks/dev.js new file mode 100644 index 000000000..6b095c606 --- /dev/null +++ b/lib/grunt/tasks/dev.js @@ -0,0 +1,9 @@ +/* jshint node: true */ +var utils = require("../utils"); + +module.exports = function(grunt){ + "use strict"; + + grunt.registerTask("dev", "Prepare code for testing", ["clean", "bower", "build", "copy:test"]); + +}; From 6d1a5bc171ae405890dccbb57d3943c3927c8f50 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 21:11:38 -0500 Subject: [PATCH 067/137] chore($grunt/tasks): #1112 - Move build task to own module --- Gruntfile.coffee | 1 - lib/grunt/tasks/build.js | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 lib/grunt/tasks/build.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 0c196eaed..7f0059e62 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -111,7 +111,6 @@ module.exports = (grunt) -> # Tasks # ========== - grunt.registerTask 'build', 'Build from latest source', ['jshint:source', 'jshint:tests', 'concat', 'minify', 'usebanner:allhead', 'usebanner:allfoot', 'copy:images'] grunt.registerTask 'build_stripped', 'Build from latest source w/ test artifacts stripped out', ['concat', 'strip_code:build', 'minify', 'usebanner:allhead', 'usebanner:allfoot', 'copy:images'] grunt.registerTask 'package', 'Build a zipped distribution-worthy version', ['build_stripped', 'copy:dist', 'shell:version_dist_templates', 'compress:jquery', 'compress:jqueryS3', 'compress:jqueryAzure', 'compress:core', 'compress:coreS3', 'compress:coreAzure' ] diff --git a/lib/grunt/tasks/build.js b/lib/grunt/tasks/build.js new file mode 100644 index 000000000..45b43a81d --- /dev/null +++ b/lib/grunt/tasks/build.js @@ -0,0 +1,20 @@ +/* jshint node: true */ +var utils = require("../utils"); + +module.exports = function(grunt){ + "use strict"; + + grunt.registerTask( + "build", + "Build from latest source", + [ + "jshint:source", + "jshint:tests", + "concat", + "minify", + "usebanner:allhead", + "usebanner:allfoot", + "copy:images" + ]); + +}; From 5f230788875bb82c365492e075296a8ee7f71383 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 21:12:05 -0500 Subject: [PATCH 068/137] chore($grunt/tasks): #1112 - Move build_stripped task to own module --- Gruntfile.coffee | 2 -- lib/grunt/tasks/build_stripped.js | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 lib/grunt/tasks/build_stripped.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 7f0059e62..8a56783f9 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -111,8 +111,6 @@ module.exports = (grunt) -> # Tasks # ========== - grunt.registerTask 'build_stripped', 'Build from latest source w/ test artifacts stripped out', ['concat', 'strip_code:build', 'minify', 'usebanner:allhead', 'usebanner:allfoot', 'copy:images'] - grunt.registerTask 'package', 'Build a zipped distribution-worthy version', ['build_stripped', 'copy:dist', 'shell:version_dist_templates', 'compress:jquery', 'compress:jqueryS3', 'compress:jqueryAzure', 'compress:core', 'compress:coreS3', 'compress:coreAzure' ] grunt.registerTask 'custom', 'Build a custom version', (modules) -> diff --git a/lib/grunt/tasks/build_stripped.js b/lib/grunt/tasks/build_stripped.js new file mode 100644 index 000000000..c0b5bede0 --- /dev/null +++ b/lib/grunt/tasks/build_stripped.js @@ -0,0 +1,19 @@ +/* jshint node: true */ +var utils = require("../utils"); + +module.exports = function(grunt){ + "use strict"; + + grunt.registerTask( + "build_stripped", + "Build from latest source w/ test artifacts stripped out", + [ + "concat", + "strip_code:build", + "minify", + "usebanner:allhead", + "usebanner:allfoot", + "copy:images" + ]); + +}; From 86dc6e1c626899b6285e852dcb206d8ccc7723a4 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 21:12:25 -0500 Subject: [PATCH 069/137] chore($grunt/tasks): #1112 - Move package task to own module --- Gruntfile.coffee | 2 -- lib/grunt/tasks/package.js | 22 ++++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 lib/grunt/tasks/package.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 8a56783f9..2dfc46d95 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -111,8 +111,6 @@ module.exports = (grunt) -> # Tasks # ========== - grunt.registerTask 'package', 'Build a zipped distribution-worthy version', ['build_stripped', 'copy:dist', 'shell:version_dist_templates', 'compress:jquery', 'compress:jqueryS3', 'compress:jqueryAzure', 'compress:core', 'compress:coreS3', 'compress:coreAzure' ] - grunt.registerTask 'custom', 'Build a custom version', (modules) -> dest = customBuildDest if (modules?) diff --git a/lib/grunt/tasks/package.js b/lib/grunt/tasks/package.js new file mode 100644 index 000000000..a17bd5568 --- /dev/null +++ b/lib/grunt/tasks/package.js @@ -0,0 +1,22 @@ +/* jshint node: true */ +var utils = require("../utils"); + +module.exports = function(grunt){ + "use strict"; + + grunt.registerTask( + "package", + "Build a zipped distribution-worthy version", + [ + "build_stripped", + "copy:dist", + "shell:version_dist_templates", + "compress:jquery", + "compress:jqueryS3", + "compress:jqueryAzure", + "compress:core", + "compress:coreS3", + "compress:coreAzure", + ]); + +}; From 81a8cb4f44e6a1534195a073d43bffe12b43c544 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 21:13:21 -0500 Subject: [PATCH 070/137] chore($grunt/tasks): #1112 - Move default task to own module --- Gruntfile.coffee | 3 --- lib/grunt/tasks/default.js | 13 +++++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 lib/grunt/tasks/default.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 2dfc46d95..e567d3c88 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -118,7 +118,4 @@ module.exports = (grunt) -> else utils.build.call utils, dest, [] grunt.task.run(['uglify:custom', 'cssmin:custom', 'strip_code:custom', 'shell:version_custom_templates', 'usebanner:customhead', 'usebanner:customfoot', 'compress:custom', 'build_details']) - - grunt.registerTask 'default', 'Default task: clean, bower, lint, build, & test', ['package'] - grunt.registerTask "server", ["nodestatic"] diff --git a/lib/grunt/tasks/default.js b/lib/grunt/tasks/default.js new file mode 100644 index 000000000..007e395ab --- /dev/null +++ b/lib/grunt/tasks/default.js @@ -0,0 +1,13 @@ +/* jshint node: true */ +var utils = require("../utils"); + +module.exports = function(grunt){ + "use strict"; + + grunt.registerTask( + "default", + "Default task: clean, bower, lint, build, & test", + ["package"] + ); + +}; From 5d4215db660d07b2170c70fa9e97e090980cdf6e Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 21:13:35 -0500 Subject: [PATCH 071/137] chore($grunt/tasks): #1112 - Move server task to own module --- Gruntfile.coffee | 1 - lib/grunt/tasks/server.js | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 lib/grunt/tasks/server.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index e567d3c88..52af5cc46 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -118,4 +118,3 @@ module.exports = (grunt) -> else utils.build.call utils, dest, [] grunt.task.run(['uglify:custom', 'cssmin:custom', 'strip_code:custom', 'shell:version_custom_templates', 'usebanner:customhead', 'usebanner:customfoot', 'compress:custom', 'build_details']) - grunt.registerTask "server", ["nodestatic"] diff --git a/lib/grunt/tasks/server.js b/lib/grunt/tasks/server.js new file mode 100644 index 000000000..e4a95f0a9 --- /dev/null +++ b/lib/grunt/tasks/server.js @@ -0,0 +1,9 @@ +/* jshint node: true */ +var utils = require("../utils"); + +module.exports = function(grunt){ + "use strict"; + + grunt.registerTask("server", ["nodestatic"]); + +}; From a5565b93691e63f63bf097c921abc82bbacf52f3 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 21:47:40 -0500 Subject: [PATCH 072/137] chore($grunt/tasks): #1112 - Move shell task to own module --- Gruntfile.coffee | 14 +------------- lib/grunt/configs/index.js | 1 + lib/grunt/configs/shell.js | 22 ++++++++++++++++++++++ 3 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 lib/grunt/configs/shell.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 52af5cc46..642a5bf12 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -68,6 +68,7 @@ module.exports = (grunt) -> cssmin: configs.cssmin(paths, customBuildDest) jshint: configs.jshint(paths) nodestatic: configs.static(paths) + shell: configs.shell(paths, customBuildDest) strip_code: configs.stripcode(paths, customBuildDest) uglify: configs.uglify(paths, customBuildDest) usebanner: configs.banner(paths, customBuildDest) @@ -82,19 +83,6 @@ module.exports = (grunt) -> local: 'karma-local.conf.coffee' travis: 'karma-travis.conf.coffee' - shell: - version_custom_templates: - command: "find #{customBuildDest}/ -type f -name '*.html' | xargs sed -i '' 's/{VERSION}/<%= pkg.version %>/'" - options: - cwd: __dirname - stderr: true - stdout: true - version_dist_templates: - command: "find #{paths.dist}/ -type f -name '*.html' | xargs sed -i '' 's/{VERSION}/<%= pkg.version %>/'" - options: - cwd: __dirname - stderr: true - stdout: true # Dependencies # ========== diff --git a/lib/grunt/configs/index.js b/lib/grunt/configs/index.js index b01659fd9..412e5efb9 100644 --- a/lib/grunt/configs/index.js +++ b/lib/grunt/configs/index.js @@ -8,6 +8,7 @@ module.exports = { copy: require("./copy"), cssmin: require("./cssmin"), jshint: require("./jshint"), + shell: require("./shell"), static: require("./static"), stripcode: require("./stripcode"), uglify: require("./uglify"), diff --git a/lib/grunt/configs/shell.js b/lib/grunt/configs/shell.js new file mode 100644 index 000000000..3024c9671 --- /dev/null +++ b/lib/grunt/configs/shell.js @@ -0,0 +1,22 @@ +/* jshint node: true */ +module.exports = function(paths, customBuildDest){ + "use strict"; + return { + version_custom_templates: { + command: "find " + customBuildDest + "/ -type f -name '*.html' | xargs sed -i '' 's/{VERSION}/<%= pkg.version %>/'", + options: { + cwd: __dirname, + stderr: true, + stdout: true + } + }, + version_dist_templates: { + command: "find " + paths.dist + "/ -type f -name '*.html' | xargs sed -i '' 's/{VERSION}/<%= pkg.version %>/'", + options: { + cwd: __dirname, + stderr: true, + stdout: true + } + } + }; +}; From 4d92a2d468146710eff63183ccef0c0adde05ec8 Mon Sep 17 00:00:00 2001 From: Mark Feltner Date: Sat, 12 Jul 2014 21:37:26 -0500 Subject: [PATCH 073/137] core($Gruntfile): #1112 - Remove bower --- Gruntfile.coffee | 1 - bower.json | 16 +- lib/grunt/configs/clean.js | 6 - lib/grunt/configs/index.js | 2 - lib/grunt/tasks/default.js | 2 +- lib/grunt/tasks/dev.js | 2 +- lib/modules.js | 10 +- package.json | 2 - test/static/third-party/assert/assert.js | 436 + .../jquery.simulate/jquery.simulate.js | 314 + test/static/third-party/jquery/jquery.js | 9800 +++++++++++++++++ test/static/third-party/json2/README | 42 + test/static/third-party/json2/cycle.js | 172 + test/static/third-party/json2/json.js | 538 + test/static/third-party/json2/json2.js | 489 + test/static/third-party/json2/json_parse.js | 349 + .../third-party/json2/json_parse_state.js | 397 + test/static/third-party/mocha/css/mocha.css | 250 + test/static/third-party/mocha/js/mocha.js | 5373 +++++++++ test/static/third-party/purl/purl.js | 265 + test/test.html | 14 +- 21 files changed, 18440 insertions(+), 40 deletions(-) create mode 100644 test/static/third-party/assert/assert.js create mode 100644 test/static/third-party/jquery.simulate/jquery.simulate.js create mode 100644 test/static/third-party/jquery/jquery.js create mode 100644 test/static/third-party/json2/README create mode 100644 test/static/third-party/json2/cycle.js create mode 100644 test/static/third-party/json2/json.js create mode 100644 test/static/third-party/json2/json2.js create mode 100644 test/static/third-party/json2/json_parse.js create mode 100644 test/static/third-party/json2/json_parse_state.js create mode 100644 test/static/third-party/mocha/css/mocha.css create mode 100644 test/static/third-party/mocha/js/mocha.js create mode 100644 test/static/third-party/purl/purl.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 642a5bf12..b9a496064 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -60,7 +60,6 @@ module.exports = (grunt) -> pkg: pkg - bower: configs.bower(paths) clean: configs.clean(paths) compress: configs.compress(paths, customBuildDest) concat: configs.concat(paths) diff --git a/bower.json b/bower.json index c53785838..c0f27f147 100644 --- a/bower.json +++ b/bower.json @@ -1,18 +1,4 @@ { "name": "fine-uploader", - "version": "5.0.3", - "devDependencies": { - "jquery": "1.10.0", - "purl": "https://github.com/allmarkedup/purl.git#~2.3.1", - "jquery.simulate": "https://github.com/jquery/jquery-simulate.git", - "json2": "latest", - "mocha": "~1.11.0", - "assert": "https://github.com/Jxck/assert.git" - }, - "exportsOverride": { - "mocha": { - "js": "mocha.js", - "css": "mocha.css" - } - } + "version": "5.0.3" } diff --git a/lib/grunt/configs/clean.js b/lib/grunt/configs/clean.js index 6b4077bbf..3a93326f4 100644 --- a/lib/grunt/configs/clean.js +++ b/lib/grunt/configs/clean.js @@ -21,12 +21,6 @@ module.exports = function(paths) { }, - vendor: { - // ./{test}/_vendor - files: { src: path.join(paths.test, "_vendor") } - - }, - custom: { // ./{custom} files: { src: paths.custom } diff --git a/lib/grunt/configs/index.js b/lib/grunt/configs/index.js index 412e5efb9..85a882ee8 100644 --- a/lib/grunt/configs/index.js +++ b/lib/grunt/configs/index.js @@ -1,7 +1,5 @@ /* jshint node: true */ module.exports = { - banner: require("./banner"), - bower: require("./bower"), clean: require("./clean"), compress: require("./compress"), concat: require("./concat"), diff --git a/lib/grunt/tasks/default.js b/lib/grunt/tasks/default.js index 007e395ab..0a95813d7 100644 --- a/lib/grunt/tasks/default.js +++ b/lib/grunt/tasks/default.js @@ -6,7 +6,7 @@ module.exports = function(grunt){ grunt.registerTask( "default", - "Default task: clean, bower, lint, build, & test", + "Default task: clean, lint, build, & test", ["package"] ); diff --git a/lib/grunt/tasks/dev.js b/lib/grunt/tasks/dev.js index 6b095c606..c8945db27 100644 --- a/lib/grunt/tasks/dev.js +++ b/lib/grunt/tasks/dev.js @@ -4,6 +4,6 @@ var utils = require("../utils"); module.exports = function(grunt){ "use strict"; - grunt.registerTask("dev", "Prepare code for testing", ["clean", "bower", "build", "copy:test"]); + grunt.registerTask("dev", "Prepare code for testing", ["clean", "build", "copy:test"]); }; diff --git a/lib/modules.js b/lib/modules.js index b8a3e5e54..bd5d886c0 100644 --- a/lib/modules.js +++ b/lib/modules.js @@ -245,11 +245,11 @@ var fineUploaderModules = { "test/functional/**/*.coffee" ], "karmaModules": [ - "test/_vendor/assert/assert.js", - "test/_vendor/jquery/jquery.js", - "test/_vendor/jquery.simulate/jquery.simulate.js", - "test/_vendor/json2/json2.js", - "test/_vendor/purl/purl.js", + "test/static/third-party/assert/assert.js", + "test/static/third-party/jquery/jquery.js", + "test/static/third-party/jquery.simulate/jquery.simulate.js", + "test/static/third-party/json2/json2.js", + "test/static/third-party/purl/purl.js", "test/static/third-party/sinon/sinon.js", "test/static/third-party/sinon/event.js", "test/static/third-party/sinon/fake_xml_http_request.js", diff --git a/package.json b/package.json index 4f503865b..9eb3e3d62 100644 --- a/package.json +++ b/package.json @@ -44,12 +44,10 @@ "devDependencies": {}, "dependencies": { "async": "0.2.9", - "bower": "1.2.5", "coffee-script": "1.6.3", "glob": "3.2.6", "grunt": "0.4.1", "grunt-banner": "0.2.0", - "grunt-bower-task": "git+https://github.com/pogoseat/grunt-bower-task.git", "grunt-contrib-clean": "0.4.1", "grunt-contrib-compress": "0.5.2", "grunt-contrib-concat": "0.3.0", diff --git a/test/static/third-party/assert/assert.js b/test/static/third-party/assert/assert.js new file mode 100644 index 000000000..b6a59eb1c --- /dev/null +++ b/test/static/third-party/assert/assert.js @@ -0,0 +1,436 @@ +// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 +// +// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! +// +// Copyright (c) 2011 Jxck +// +// Originally from node.js (http://nodejs.org) +// Copyright Joyent, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the 'Software'), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +(function(global) { + +// Object.create compatible in IE +var create = Object.create || function(p) { + if (!p) throw Error('no type'); + function f() {}; + f.prototype = p; + return new f(); +}; + +// UTILITY +var util = { + inherits: function(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true + } + }); + }, + isArray: function(ar) { + return Array.isArray(ar); + }, + isBoolean: function(arg) { + return typeof arg === 'boolean'; + }, + isNull: function(arg) { + return arg === null; + }, + isNullOrUndefined: function(arg) { + return arg == null; + }, + isNumber: function(arg) { + return typeof arg === 'number'; + }, + isString: function(arg) { + return typeof arg === 'string'; + }, + isSymbol: function(arg) { + return typeof arg === 'symbol'; + }, + isUndefined: function(arg) { + return arg === void 0; + }, + isRegExp: function(re) { + return util.isObject(re) && util.objectToString(re) === '[object RegExp]'; + }, + isObject: function(arg) { + return typeof arg === 'object' && arg !== null; + }, + isDate: function(d) { + return util.isObject(d) && util.objectToString(d) === '[object Date]'; + }, + isError: function(e) { + return isObject(e) && + (objectToString(e) === '[object Error]' || e instanceof Error); + }, + isFunction: function(arg) { + return typeof arg === 'function'; + }, + isPrimitive: function(arg) { + return arg === null || + typeof arg === 'boolean' || + typeof arg === 'number' || + typeof arg === 'string' || + typeof arg === 'symbol' || // ES6 symbol + typeof arg === 'undefined'; + }, + objectToString: function(o) { + return Object.prototype.toString.call(o); + } +}; + +var pSlice = Array.prototype.slice; + +// from https://github.com/substack/node-deep-equal +var Object_keys = typeof Object.keys === 'function' + ? Object.keys + : function (obj) { + var keys = []; + for (var key in obj) keys.push(key); + return keys; + } +; + +// 1. The assert module provides functions that throw +// AssertionError's when particular conditions are not met. The +// assert module must conform to the following interface. + +var assert = ok; + +global['assert'] = assert; + +if (typeof module === 'object' && typeof module.exports === 'object') { + module.exports = assert; +}; + +// 2. The AssertionError is defined in assert. +// new assert.AssertionError({ message: message, +// actual: actual, +// expected: expected }) + +assert.AssertionError = function AssertionError(options) { + this.name = 'AssertionError'; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } else { + // try to throw an error now, and from the stack property + // work out the line that called in to assert.js. + try { + this.stack = (new Error).stack.toString(); + } catch (e) {} + } +}; + +// assert.AssertionError instanceof Error +util.inherits(assert.AssertionError, Error); + +function replacer(key, value) { + if (util.isUndefined(value)) { + return '' + value; + } + if (util.isNumber(value) && (isNaN(value) || !isFinite(value))) { + return value.toString(); + } + if (util.isFunction(value) || util.isRegExp(value)) { + return value.toString(); + } + return value; +} + +function truncate(s, n) { + if (util.isString(s)) { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } +} + +function getMessage(self) { + return truncate(JSON.stringify(self.actual, replacer), 128) + ' ' + + self.operator + ' ' + + truncate(JSON.stringify(self.expected, replacer), 128); +} + +// At present only the three keys mentioned above are used and +// understood by the spec. Implementations or sub modules can pass +// other keys to the AssertionError's constructor - they will be +// ignored. + +// 3. All of the following functions must throw an AssertionError +// when a corresponding condition is not met, with a message that +// may be undefined if not provided. All assertion methods provide +// both the actual and expected values to the assertion error for +// display purposes. + +function fail(actual, expected, message, operator, stackStartFunction) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction + }); +} + +// EXTENSION! allows for well behaved errors defined elsewhere. +assert.fail = fail; + +// 4. Pure assertion tests whether a value is truthy, as determined +// by !!guard. +// assert.ok(guard, message_opt); +// This statement is equivalent to assert.equal(true, !!guard, +// message_opt);. To test strictly for the value true, use +// assert.strictEqual(true, guard, message_opt);. + +function ok(value, message) { + if (!value) fail(value, true, message, '==', assert.ok); +} +assert.ok = ok; + +// 5. The equality assertion tests shallow, coercive equality with +// ==. +// assert.equal(actual, expected, message_opt); + +assert.equal = function equal(actual, expected, message) { + if (actual != expected) fail(actual, expected, message, '==', assert.equal); +}; + +// 6. The non-equality assertion tests for whether two objects are not equal +// with != assert.notEqual(actual, expected, message_opt); + +assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, '!=', assert.notEqual); + } +}; + +// 7. The equivalence assertion tests a deep equality relation. +// assert.deepEqual(actual, expected, message_opt); + +assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected)) { + fail(actual, expected, message, 'deepEqual', assert.deepEqual); + } +}; + +function _deepEqual(actual, expected) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + + // } else if (util.isBuffer(actual) && util.isBuffer(expected)) { + // if (actual.length != expected.length) return false; + // + // for (var i = 0; i < actual.length; i++) { + // if (actual[i] !== expected[i]) return false; + // } + // + // return true; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if (!util.isObject(actual) && !util.isObject(expected)) { + return actual == expected; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else { + return objEquiv(actual, expected); + } +} + +function isArguments(object) { + return Object.prototype.toString.call(object) == '[object Arguments]'; +} + +function objEquiv(a, b) { + if (util.isNullOrUndefined(a) || util.isNullOrUndefined(b)) + return false; + // an identical 'prototype' property. + if (a.prototype !== b.prototype) return false; + //~~~I've managed to break Object.keys through screwy arguments passing. + // Converting to array solves the problem. + if (isArguments(a)) { + if (!isArguments(b)) { + return false; + } + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b); + } + try { + var ka = Object_keys(a), + kb = Object_keys(b), + key, i; + } catch (e) {//happens when one is a string literal and the other isn't + return false; + } + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length != kb.length) + return false; + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] != kb[i]) + return false; + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key])) return false; + } + return true; +} + +// 8. The non-equivalence assertion tests for any deep inequality. +// assert.notDeepEqual(actual, expected, message_opt); + +assert.notDeepEqual = function notDeepEqual(actual, expected, message) { + if (_deepEqual(actual, expected)) { + fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); + } +}; + +// 9. The strict equality assertion tests strict equality, as determined by ===. +// assert.strictEqual(actual, expected, message_opt); + +assert.strictEqual = function strictEqual(actual, expected, message) { + if (actual !== expected) { + fail(actual, expected, message, '===', assert.strictEqual); + } +}; + +// 10. The strict non-equality assertion tests for strict inequality, as +// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + +assert.notStrictEqual = function notStrictEqual(actual, expected, message) { + if (actual === expected) { + fail(actual, expected, message, '!==', assert.notStrictEqual); + } +}; + +function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if (Object.prototype.toString.call(expected) == '[object RegExp]') { + return expected.test(actual); + } else if (actual instanceof expected) { + return true; + } else if (expected.call({}, actual) === true) { + return true; + } + + return false; +} + +function _throws(shouldThrow, block, expected, message) { + var actual; + + if (util.isString(expected)) { + message = expected; + expected = null; + } + + try { + block(); + } catch (e) { + actual = e; + } + + message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + + (message ? ' ' + message : '.'); + + if (shouldThrow && !actual) { + fail(actual, expected, 'Missing expected exception' + message); + } + + if (!shouldThrow && expectedException(actual, expected)) { + fail(actual, expected, 'Got unwanted exception' + message); + } + + if ((shouldThrow && actual && expected && + !expectedException(actual, expected)) || (!shouldThrow && actual)) { + throw actual; + } +} + +// 11. Expected to throw an error: +// assert.throws(block, Error_opt, message_opt); + +assert.throws = function(block, /*optional*/error, /*optional*/message) { + _throws.apply(this, [true].concat(pSlice.call(arguments))); +}; + +// EXTENSION! This is annoying to write outside this module. +assert.doesNotThrow = function(block, /*optional*/message) { + _throws.apply(this, [false].concat(pSlice.call(arguments))); +}; + +assert.ifError = function(err) { if (err) {throw err;}}; + +if (typeof define === 'function' && define.amd) { + define('assert', function () { + return assert; + }); +} + +})(this); + diff --git a/test/static/third-party/jquery.simulate/jquery.simulate.js b/test/static/third-party/jquery.simulate/jquery.simulate.js new file mode 100644 index 000000000..eeb5624e8 --- /dev/null +++ b/test/static/third-party/jquery.simulate/jquery.simulate.js @@ -0,0 +1,314 @@ + /*! + * jQuery Simulate v@VERSION - simulate browser mouse and keyboard events + * https://github.com/jquery/jquery-simulate + * + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * Date: @DATE + */ + +;(function( $, undefined ) { + +var rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/; + +$.fn.simulate = function( type, options ) { + return this.each(function() { + new $.simulate( this, type, options ); + }); +}; + +$.simulate = function( elem, type, options ) { + var method = $.camelCase( "simulate-" + type ); + + this.target = elem; + this.options = options; + + if ( this[ method ] ) { + this[ method ](); + } else { + this.simulateEvent( elem, type, options ); + } +}; + +$.extend( $.simulate, { + + keyCode: { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + NUMPAD_ADD: 107, + NUMPAD_DECIMAL: 110, + NUMPAD_DIVIDE: 111, + NUMPAD_ENTER: 108, + NUMPAD_MULTIPLY: 106, + NUMPAD_SUBTRACT: 109, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38 + }, + + buttonCode: { + LEFT: 0, + MIDDLE: 1, + RIGHT: 2 + } +}); + +$.extend( $.simulate.prototype, { + + simulateEvent: function( elem, type, options ) { + var event = this.createEvent( type, options ); + this.dispatchEvent( elem, type, event, options ); + }, + + createEvent: function( type, options ) { + if ( rkeyEvent.test( type ) ) { + return this.keyEvent( type, options ); + } + + if ( rmouseEvent.test( type ) ) { + return this.mouseEvent( type, options ); + } + }, + + mouseEvent: function( type, options ) { + var event, eventDoc, doc, body; + options = $.extend({ + bubbles: true, + cancelable: (type !== "mousemove"), + view: window, + detail: 0, + screenX: 0, + screenY: 0, + clientX: 1, + clientY: 1, + ctrlKey: false, + altKey: false, + shiftKey: false, + metaKey: false, + button: 0, + relatedTarget: undefined + }, options ); + + if ( document.createEvent ) { + event = document.createEvent( "MouseEvents" ); + event.initMouseEvent( type, options.bubbles, options.cancelable, + options.view, options.detail, + options.screenX, options.screenY, options.clientX, options.clientY, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, + options.button, options.relatedTarget || document.body.parentNode ); + + // IE 9+ creates events with pageX and pageY set to 0. + // Trying to modify the properties throws an error, + // so we define getters to return the correct values. + if ( event.pageX === 0 && event.pageY === 0 && Object.defineProperty ) { + eventDoc = event.relatedTarget.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + Object.defineProperty( event, "pageX", { + get: function() { + return options.clientX + + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - + ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + } + }); + Object.defineProperty( event, "pageY", { + get: function() { + return options.clientY + + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - + ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + }); + } + } else if ( document.createEventObject ) { + event = document.createEventObject(); + $.extend( event, options ); + // standards event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ff974877(v=vs.85).aspx + // old IE event.button uses constants defined here: http://msdn.microsoft.com/en-us/library/ie/ms533544(v=vs.85).aspx + // so we actually need to map the standard back to oldIE + event.button = { + 0: 1, + 1: 4, + 2: 2 + }[ event.button ] || event.button; + } + + return event; + }, + + keyEvent: function( type, options ) { + var event; + options = $.extend({ + bubbles: true, + cancelable: true, + view: window, + ctrlKey: false, + altKey: false, + shiftKey: false, + metaKey: false, + keyCode: 0, + charCode: undefined + }, options ); + + if ( document.createEvent ) { + try { + event = document.createEvent( "KeyEvents" ); + event.initKeyEvent( type, options.bubbles, options.cancelable, options.view, + options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, + options.keyCode, options.charCode ); + // initKeyEvent throws an exception in WebKit + // see: http://stackoverflow.com/questions/6406784/initkeyevent-keypress-only-works-in-firefox-need-a-cross-browser-solution + // and also https://bugs.webkit.org/show_bug.cgi?id=13368 + // fall back to a generic event until we decide to implement initKeyboardEvent + } catch( err ) { + event = document.createEvent( "Events" ); + event.initEvent( type, options.bubbles, options.cancelable ); + $.extend( event, { + view: options.view, + ctrlKey: options.ctrlKey, + altKey: options.altKey, + shiftKey: options.shiftKey, + metaKey: options.metaKey, + keyCode: options.keyCode, + charCode: options.charCode + }); + } + } else if ( document.createEventObject ) { + event = document.createEventObject(); + $.extend( event, options ); + } + + if ( !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ) || (({}).toString.call( window.opera ) === "[object Opera]") ) { + event.keyCode = (options.charCode > 0) ? options.charCode : options.keyCode; + event.charCode = undefined; + } + + return event; + }, + + dispatchEvent: function( elem, type, event ) { + if ( elem[ type ] ) { + elem[ type ](); + } else if ( elem.dispatchEvent ) { + elem.dispatchEvent( event ); + } else if ( elem.fireEvent ) { + elem.fireEvent( "on" + type, event ); + } + }, + + simulateFocus: function() { + var focusinEvent, + triggered = false, + element = $( this.target ); + + function trigger() { + triggered = true; + } + + element.bind( "focus", trigger ); + element[ 0 ].focus(); + + if ( !triggered ) { + focusinEvent = $.Event( "focusin" ); + focusinEvent.preventDefault(); + element.trigger( focusinEvent ); + element.triggerHandler( "focus" ); + } + element.unbind( "focus", trigger ); + }, + + simulateBlur: function() { + var focusoutEvent, + triggered = false, + element = $( this.target ); + + function trigger() { + triggered = true; + } + + element.bind( "blur", trigger ); + element[ 0 ].blur(); + + // blur events are async in IE + setTimeout(function() { + // IE won't let the blur occur if the window is inactive + if ( element[ 0 ].ownerDocument.activeElement === element[ 0 ] ) { + element[ 0 ].ownerDocument.body.focus(); + } + + // Firefox won't trigger events if the window is inactive + // IE doesn't trigger events if we had to manually focus the body + if ( !triggered ) { + focusoutEvent = $.Event( "focusout" ); + focusoutEvent.preventDefault(); + element.trigger( focusoutEvent ); + element.triggerHandler( "blur" ); + } + element.unbind( "blur", trigger ); + }, 1 ); + } +}); + + + +/** complex events **/ + +function findCenter( elem ) { + var offset, + document = $( elem.ownerDocument ); + elem = $( elem ); + offset = elem.offset(); + + return { + x: offset.left + elem.outerWidth() / 2 - document.scrollLeft(), + y: offset.top + elem.outerHeight() / 2 - document.scrollTop() + }; +} + +$.extend( $.simulate.prototype, { + simulateDrag: function() { + var i = 0, + target = this.target, + options = this.options, + center = findCenter( target ), + x = Math.floor( center.x ), + y = Math.floor( center.y ), + dx = options.dx || 0, + dy = options.dy || 0, + moves = options.moves || 3, + coord = { clientX: x, clientY: y }; + + this.simulateEvent( target, "mousedown", coord ); + + for ( ; i < moves ; i++ ) { + x += dx / moves; + y += dy / moves; + + coord = { + clientX: Math.round( x ), + clientY: Math.round( y ) + }; + + this.simulateEvent( document, "mousemove", coord ); + } + + this.simulateEvent( target, "mouseup", coord ); + this.simulateEvent( target, "click", coord ); + } +}); + +})( jQuery ); diff --git a/test/static/third-party/jquery/jquery.js b/test/static/third-party/jquery/jquery.js new file mode 100644 index 000000000..a4b8b8a2c --- /dev/null +++ b/test/static/third-party/jquery/jquery.js @@ -0,0 +1,9800 @@ +/*! + * jQuery JavaScript Library v1.10.0 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-05-24T18:39Z + */ +(function( window, undefined ) { + +// Can't do this because several apps including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// Support: Firefox 18+ +//"use strict"; +var + // The deferred used on DOM ready + readyList, + + // A central reference to the root jQuery(document) + rootjQuery, + + // Support: IE<10 + // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined` + core_strundefined = typeof undefined, + + // Use the correct document accordingly with window argument (sandbox) + location = window.location, + document = window.document, + docElem = document.documentElement, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // [[Class]] -> type pairs + class2type = {}, + + // List of deleted data cache ids, so we can reuse them + core_deletedIds = [], + + core_version = "1.10.0", + + // Save a reference to some core methods + core_concat = core_deletedIds.concat, + core_push = core_deletedIds.push, + core_slice = core_deletedIds.slice, + core_indexOf = core_deletedIds.indexOf, + core_toString = class2type.toString, + core_hasOwn = class2type.hasOwnProperty, + core_trim = core_version.trim, + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Used for matching numbers + core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, + + // Used for splitting on whitespace + core_rnotwhite = /\S+/g, + + // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, + rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }, + + // The ready event handler + completed = function( event ) { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) { + detach(); + jQuery.ready(); + } + }, + // Clean-up method for dom ready events + detach = function() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: core_version, + + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return core_slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; + }, + + slice: function() { + return this.pushStack( core_slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: core_push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), + + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger("ready").off("ready"); + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + if ( obj == null ) { + return String( obj ); + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ core_toString.call(obj) ] || "object" : + typeof obj; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !core_hasOwn.call(obj, "constructor") && + !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( jQuery.support.ownLast ) { + for ( key in obj ) { + return core_hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || core_hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + // data: string of html + // context (optional): If specified, the fragment will be created in this context, defaults to document + // keepScripts (optional): If true, will include scripts passed in the html string + parseHTML: function( data, context, keepScripts ) { + if ( !data || typeof data !== "string" ) { + return null; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + context = context || document; + + var parsed = rsingleTag.exec( data ), + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[1] ) ]; + } + + parsed = jQuery.buildFragment( [ data ], context, scripts ); + if ( scripts ) { + jQuery( scripts ).remove(); + } + return jQuery.merge( [], parsed.childNodes ); + }, + + parseJSON: function( data ) { + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + if ( data === null ) { + return data; + } + + if ( typeof data === "string" ) { + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + if ( data ) { + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + } + } + } + + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + var xml, tmp; + if ( !data || typeof data !== "string" ) { + return null; + } + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Use native String.trim function wherever possible + trim: core_trim && !core_trim.call("\uFEFF\xA0") ? + function( text ) { + return text == null ? + "" : + core_trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + core_push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( core_indexOf ) { + return core_indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var l = second.length, + i = first.length, + j = 0; + + if ( typeof l === "number" ) { + for ( ; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var retVal, + ret = [], + i = 0, + length = elems.length; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return core_concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = core_slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + // Multifunctional method to get and set values of a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations. + // Note: this method belongs to the css module but it's needed here for the support module. + // If support gets modularized, this method should be moved back to the css module. + swap: function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + + // If IE event model is used + } else { + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch(e) {} + + if ( top && top.doScroll ) { + (function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll("left"); + } catch(e) { + return setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + })(); + } + } + } + return readyList.promise( obj ); +}; + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + var length = obj.length, + type = jQuery.type( obj ); + + if ( jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || type !== "function" && + ( length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj ); +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); +/*! + * Sizzle CSS Selector Engine v1.9.4-pre + * http://sizzlejs.com/ + * + * Copyright 2013 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2013-05-15 + */ +(function( window, undefined ) { + +var i, + support, + cachedruns, + Expr, + getText, + isXML, + compile, + outermostContext, + sortInput, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + -(new Date()), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + hasDuplicate = false, + sortOrder = function() { return 0; }, + + // General-purpose constants + strundefined = typeof undefined, + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf if we can't use a native one + indexOf = arr.indexOf || function( elem ) { + var i = 0, + len = this.length; + for ( ; i < len; i++ ) { + if ( this[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + + "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + + // Prefer arguments quoted, + // then not containing pseudos/brackets, + // then attribute selectors/non-parenthetical expressions, + // then anything else + // These preferences are here to reduce the number of selectors + // needing tokenize in the PSEUDO preFilter + pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rsibling = new RegExp( whitespace + "*[+~]" ), + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + // BMP codepoint + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { + return []; + } + + if ( documentIsHTML && !seed ) { + + // Shortcuts + if ( (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType === 9 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && context.parentNode || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * For feature detection + * @param {Function} fn The function to test for native support + */ +function isNative( fn ) { + return rnative.test( fn + "" ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key += " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied if the test fails + * @param {Boolean} test The result of a test. If true, null will be set as the handler in leiu of the specified handler + */ +function addHandle( attrs, handler, test ) { + attrs = attrs.split("|"); + var current, + i = attrs.length, + setHandle = test ? null : handler; + + while ( i-- ) { + // Don't override a user's handler + if ( !(current = Expr.attrHandle[ attrs[i] ]) || current === handler ) { + Expr.attrHandle[ attrs[i] ] = setHandle; + } + } +} + +/** + * Fetches boolean attributes by node + * @param {Element} elem + * @param {String} name + */ +function boolHandler( elem, name ) { + // XML does not need to be checked as this will not be assigned for XML documents + var val = elem.getAttributeNode( name ); + return val && val.specified ? + val.value : + elem[ name ] === true ? name.toLowerCase() : null; +} + +/** + * Fetches attributes without interpolation + * http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx + * @param {Element} elem + * @param {String} name + */ +function interpolationHandler( elem, name ) { + // XML does not need to be checked as this will not be assigned for XML documents + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); +} + +/** + * Uses defaultValue to retrieve value in IE6/7 + * @param {Element} elem + * @param {String} name + */ +function valueHandler( elem ) { + // Ignore the value *property* on inputs by using defaultValue + // Fallback to Sizzle.attr by returning undefined where appropriate + // XML does not need to be checked as this will not be assigned for XML documents + if ( elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns Returns -1 if a precedes b, 1 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Detect xml + * @param {Element|Object} elem An element or a document + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var doc = node ? node.ownerDocument || node : preferredDoc; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + + // Support tests + documentIsHTML = !isXML( doc ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) + support.attributes = assert(function( div ) { + + // Support: IE<8 + // Prevent attribute/property "interpolation" + div.innerHTML = ""; + addHandle( "type|href|height|width", interpolationHandler, div.firstChild.getAttribute("href") === "#" ); + + // Support: IE<9 + // Use getAttributeNode to fetch booleans when getAttribute lies + addHandle( booleans, boolHandler, div.getAttribute("disabled") == null ); + + div.className = "i"; + return !div.getAttribute("className"); + }); + + // Support: IE<9 + // Retrieving value should defer to defaultValue + support.input = assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; + }); + + // IE6/7 still return empty string for value, + // but are actually retrieving the property + addHandle( "value", valueHandler, support.attributes && support.input ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Check if getElementsByClassName can be trusted + support.getElementsByClassName = assert(function( div ) { + div.innerHTML = "
"; + + // Support: Safari<4 + // Catch class over-caching + div.firstChild.className = "i"; + // Support: Opera<10 + // Catch gEBCN failure to find non-leading classes + return div.getElementsByClassName("i").length === 2; + }); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== strundefined && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== strundefined ) { + return context.getElementsByTagName( tag ); + } + } : + function( tag, context ) { + var elem, + tmp = [], + i = 0, + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = isNative(doc.querySelectorAll)) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + div.innerHTML = ""; + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + }); + + assert(function( div ) { + + // Support: Opera 10-12/IE8 + // ^= $= *= and empty values + // Should not select anything + // Support: Windows 8 Native Apps + // The type attribute is restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "t", "" ); + + if ( div.querySelectorAll("[t^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = isNative( (matches = docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = isNative(docElem.contains) || docElem.compareDocumentPosition ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) + // Detached nodes confoundingly follow *each other* + support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( doc.createElement("div") ) & 1; + }); + + // Document order sorting + sortOrder = docElem.compareDocumentPosition ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); + + if ( compare ) { + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } + + // Not directly comparable, sort on existence of method + return a.compareDocumentPosition ? -1 : 1; + } : + function( a, b ) { + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Parentless nodes are either documents or disconnected + } else if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch(e) {} + } + + return Sizzle( expr, document, null, [elem] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = ( fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined ); + + return val === undefined ? + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null : + val; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + for ( ; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (see #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[5] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] && match[4] !== undefined ) { + match[2] = match[4]; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf.call( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), + // not comment, processing instructions, or others + // Thanks to Diego Perini for the nodeName shortcut + // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +function tokenize( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( tokens = [] ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var data, cache, outerCache, + dirkey = dirruns + " " + doneName; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { + if ( (data = cache[1]) === true || data === cachedruns ) { + return data === true; + } + } else { + cache = outerCache[ dir ] = [ dirkey ]; + cache[1] = matcher( elem, context, xml ) || cachedruns; + if ( cache[1] === true ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf.call( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + // A counter to specify which element is currently being matched + var matcherCachedRuns = 0, + bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, expandContext ) { + var elem, j, matcher, + setMatched = [], + matchedCount = 0, + i = "0", + unmatched = seed && [], + outermost = expandContext != null, + contextBackup = outermostContext, + // We must always have either seed elements or context + elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); + + if ( outermost ) { + outermostContext = context !== document && context; + cachedruns = matcherCachedRuns; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + for ( ; (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + cachedruns = ++matcherCachedRuns; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !group ) { + group = tokenize( selector ); + } + i = group.length; + while ( i-- ) { + cached = matcherFromTokens( group[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + } + return cached; +}; + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function select( selector, context, results, seed ) { + var i, tokens, token, type, find, + match = tokenize( selector ); + + if ( !seed ) { + // Try to minimize operations if there is only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + } + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && context.parentNode || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + } + + // Compile and execute a filtering function + // Provide `match` to avoid retokenization if we modified the selector above + compile( selector, match )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) + ); + return results; +} + +// Deprecated +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Initialize against the default document +setDocument(); + +// Support: Chrome<<14 +// Always assume duplicates if they aren't passed to the comparison function +[0, 0].sort( sortOrder ); +support.detectDuplicates = hasDuplicate; + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})( window ); +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // First callback to fire (used internally by add and fireWith) + firingStart, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( list && ( !fired || stack ) ) { + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var action = tuple[ 0 ], + fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = core_slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; + if( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); +jQuery.support = (function( support ) { + + var all, a, input, select, fragment, opt, eventName, isSupported, i, + div = document.createElement("div"); + + // Setup + div.setAttribute( "className", "t" ); + div.innerHTML = "
a"; + + // Finish early in limited (non-browser) environments + all = div.getElementsByTagName("*") || []; + a = div.getElementsByTagName("a")[ 0 ]; + if ( !a || !a.style || !all.length ) { + return support; + } + + // First batch of tests + select = document.createElement("select"); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName("input")[ 0 ]; + + a.style.cssText = "top:1px;float:left;opacity:.5"; + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + support.getSetAttribute = div.className !== "t"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName("tbody").length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName("link").length; + + // Get the style information from getAttribute + // (IE uses .cssText instead) + support.style = /top/.test( a.getAttribute("style") ); + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + support.hrefNormalized = a.getAttribute("href") === "/a"; + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + support.opacity = /^0.5/.test( a.style.opacity ); + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + support.cssFloat = !!a.style.cssFloat; + + // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere) + support.checkOn = !!input.value; + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + support.optSelected = opt.selected; + + // Tests for enctype support on a form (#6743) + support.enctype = !!document.createElement("form").enctype; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>"; + + // Will be defined later + support.inlineBlockNeedsLayout = false; + support.shrinkWrapBlocks = false; + support.pixelPosition = false; + support.deleteExpando = true; + support.noCloneEvent = true; + support.reliableMarginRight = true; + support.boxSizingReliable = true; + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Support: IE<9 + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + // Check if we can trust getAttribute("value") + input = document.createElement("input"); + input.setAttribute( "value", "" ); + support.input = input.getAttribute( "value" ) === ""; + + // Check if an input maintains its value after becoming a radio + input.value = "t"; + input.setAttribute( "type", "radio" ); + support.radioValue = input.value === "t"; + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "checked", "t" ); + input.setAttribute( "name", "t" ); + + fragment = document.createDocumentFragment(); + fragment.appendChild( input ); + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Opera does not clone events (and typeof div.attachEvent === undefined). + // IE9-10 clones events bound via attachEvent, but they don't trigger with .click() + if ( div.attachEvent ) { + div.attachEvent( "onclick", function() { + support.noCloneEvent = false; + }); + + div.cloneNode( true ).click(); + } + + // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event) + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + for ( i in { submit: true, change: true, focusin: true }) { + div.setAttribute( eventName = "on" + i, "t" ); + + support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false; + } + + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + // Support: IE<9 + // Iteration over object's inherited properties before its own. + for ( i in jQuery( support ) ) { + break; + } + support.ownLast = i !== "0"; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, marginDiv, tds, + divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;", + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + container = document.createElement("div"); + container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + + body.appendChild( container ).appendChild( div ); + + // Support: IE8 + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + div.innerHTML = "
t
"; + tds = div.getElementsByTagName("td"); + tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none"; + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Support: IE8 + // Check if empty table cells still have offsetWidth/Height + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check box-sizing and margin behavior. + div.innerHTML = ""; + div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;"; + + // Workaround failing boxSizing test due to offsetWidth returning wrong value + // with some non-1 values of body zoom, ticket #13543 + jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() { + support.boxSizing = div.offsetWidth === 4; + }); + + // Use window.getComputedStyle because jsdom on node.js will break without it. + if ( window.getComputedStyle ) { + support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; + support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. (#3333) + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + marginDiv = div.appendChild( document.createElement("div") ); + marginDiv.style.cssText = div.style.cssText = divReset; + marginDiv.style.marginRight = marginDiv.style.width = "0"; + div.style.width = "1px"; + + support.reliableMarginRight = + !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + } + + if ( typeof div.style.zoom !== core_strundefined ) { + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.innerHTML = ""; + div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1"; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + div.style.display = "block"; + div.innerHTML = "
"; + div.firstChild.style.width = "5px"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + + if ( support.inlineBlockNeedsLayout ) { + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); + + // Null elements to avoid leaks in IE + container = div = tds = marginDiv = null; + }); + + // Null elements to avoid leaks in IE + all = select = fragment = opt = a = input = null; + + return support; +})({}); + +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + rmultiDash = /([A-Z])/g; + +function internalData( elem, name, data, pvt /* Internal Use Only */ ){ + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = core_deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split(" "); + } + } + } else { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( jQuery.support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, null + } else { + cache[ id ] = null; + } +} + +jQuery.extend({ + cache: {}, + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "applet": true, + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + // Do not set data on non-element because it will not be cleared (#8335). + if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) { + return false; + } + + var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ]; + + // nodes accept data unless otherwise specified; rejection can be conditional + return !noData || noData !== true && elem.getAttribute("classid") === noData; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var attrs, name, + data = null, + i = 0, + elem = this[0]; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attrs = elem.attributes; + for ( ; i < attrs.length; i++ ) { + name = attrs[i].name; + + if ( name.indexOf("data-") === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + return arguments.length > 1 ? + + // Sets one value + this.each(function() { + jQuery.data( this, key, value ); + }) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null; + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray(data) ) { + queue = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + hooks.cur = fn; + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var nodeHook, boolHook, + rclass = /[\t\r\n\f]/g, + rreturn = /\r/g, + rfocusable = /^(?:input|select|textarea|button|object)$/i, + rclickable = /^(?:a|area)$/i, + ruseDefault = /^(?:checked|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + getSetInput = jQuery.support.input; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call( this, j, this.className ) ); + }); + } + + if ( proceed ) { + // The disjunction here is for better compressibility (see removeClass) + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + " " + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + elem.className = jQuery.trim( cur ); + + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, clazz, j, + i = 0, + len = this.length, + proceed = arguments.length === 0 || typeof value === "string" && value; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call( this, j, this.className ) ); + }); + } + if ( proceed ) { + classes = ( value || "" ).match( core_rnotwhite ) || []; + + for ( ; i < len; i++ ) { + elem = this[ i ]; + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( elem.className ? + ( " " + elem.className + " " ).replace( rclass, " " ) : + "" + ); + + if ( cur ) { + j = 0; + while ( (clazz = classes[j++]) ) { + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + elem.className = value ? jQuery.trim( cur ) : ""; + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.match( core_rnotwhite ) || []; + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space separated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + // Toggle whole class name + } else if ( type === core_strundefined || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // If the element has a class name or if we're passed "false", + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var ret, hooks, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // Use proper attribute retrieval(#6932, #12072) + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + elem.text; + } + }, + select: { + get: function( elem ) { + var value, option, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one" || index < 0, + values = one ? null : [], + max = one ? index + 1 : options.length, + i = index < 0 ? + max : + one ? index : 0; + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // oldIE doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + // Don't return options that are disabled or in a disabled optgroup + ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && + ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { + optionSet = true; + } + } + + // force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attr: function( elem, name, value ) { + var hooks, ret, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === core_strundefined ) { + return jQuery.prop( elem, name, value ); + } + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + + } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, value + "" ); + return value; + } + + } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var name, propName, + i = 0, + attrNames = value && value.match( core_rnotwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( (name = attrNames[i++]) ) { + propName = jQuery.propFix[ name ] || name; + + // Boolean attributes get special treatment (#10870) + if ( jQuery.expr.match.bool.test( name ) ) { + // Set corresponding property to false + if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + elem[ propName ] = false; + // Support: IE<9 + // Also clear defaultChecked/defaultSelected (if appropriate) + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = + elem[ propName ] = false; + } + + // See #9699 for explanation of this approach (setting first, then removal) + } else { + jQuery.attr( elem, name, "" ); + } + + elem.removeAttribute( getSetAttribute ? name : propName ); + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to default in case type is set after value during creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? + ret : + ( elem[ name ] = value ); + + } else { + return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? + ret : + elem[ name ]; + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + return tabindex ? + parseInt( tabindex, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + -1; + } + } + } +}); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) { + // IE<8 needs the *property* name + elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name ); + + // Use defaultChecked and defaultSelected for oldIE + } else { + elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true; + } + + return name; + } +}; +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; + + jQuery.expr.attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( name ) ? + function( elem, name, isXML ) { + var fn = jQuery.expr.attrHandle[ name ], + ret = isXML ? + undefined : + /* jshint eqeqeq: false */ + (jQuery.expr.attrHandle[ name ] = undefined) != + getter( elem, name, isXML ) ? + + name.toLowerCase() : + null; + jQuery.expr.attrHandle[ name ] = fn; + return ret; + } : + function( elem, name, isXML ) { + return isXML ? + undefined : + elem[ jQuery.camelCase( "default-" + name ) ] ? + name.toLowerCase() : + null; + }; +}); + +// fix oldIE attroperties +if ( !getSetInput || !getSetAttribute ) { + jQuery.attrHooks.value = { + set: function( elem, value, name ) { + if ( jQuery.nodeName( elem, "input" ) ) { + // Does not return so that setAttribute is also used + elem.defaultValue = value; + } else { + // Use nodeHook if defined (#1954); otherwise setAttribute is fine + return nodeHook && nodeHook.set( elem, value, name ); + } + } + }; +} + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = { + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + elem.setAttributeNode( + (ret = elem.ownerDocument.createAttribute( name )) + ); + } + + ret.value = value += ""; + + // Break association with cloned elements by also using setAttribute (#9646) + return name === "value" || value === elem.getAttribute( name ) ? + value : + undefined; + } + }; + jQuery.expr.attrHandle.id = jQuery.expr.attrHandle.name = jQuery.expr.attrHandle.coords = + // Some attributes are constructed with empty-string values when not defined + function( elem, name, isXML ) { + var ret; + return isXML ? + undefined : + (ret = elem.getAttributeNode( name )) && ret.value !== "" ? + ret.value : + null; + }; + jQuery.valHooks.button = { + get: function( elem, name ) { + var ret = elem.getAttributeNode( name ); + return ret && ret.specified ? + ret.value : + undefined; + }, + set: nodeHook.set + }; + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + set: function( elem, value, name ) { + nodeHook.set( elem, value === "" ? false : value, name ); + } + }; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }; + }); +} + + +// Some attributes require a special call on IE +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !jQuery.support.hrefNormalized ) { + // href/src property should get the full normalized URL (#10299/#12915) + jQuery.each([ "href", "src" ], function( i, name ) { + jQuery.propHooks[ name ] = { + get: function( elem ) { + return elem.getAttribute( name, 4 ); + } + }; + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Note: IE uppercases css property names, but if we were to .toLowerCase() + // .cssText, that would destroy case senstitivity in URL's, like in "background" + return elem.style.cssText || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = value + "" ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }; +} + +jQuery.each([ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +}); + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }; + if ( !jQuery.support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + // Support: Webkit + // "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + }; + } +}); +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( core_rnotwhite ) || [""]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = core_hasOwn.call( event, "type" ) ? event.type : event, + namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, ret, handleObj, matched, j, + handlerQueue = [], + args = core_slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var sel, handleObj, matches, i, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Chrome 23+, Safari? + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Even when returnValue equals to undefined Firefox will still show alert + if ( event.result !== undefined ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, to properly expose it to GC + if ( typeof elem[ name ] === core_strundefined ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + if ( !e ) { + return; + } + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !jQuery._data( form, "submitBubbles" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + jQuery._data( form, "submitBubbles", true ); + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + } + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event, true ); + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + jQuery._data( elem, "changeBubbles", true ); + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var type, origFn; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); +var isSimple = /^.[^:#\[\.,]*$/, + rparentsprev = /^(?:parents|prev(?:Until|All))/, + rneedsContext = jQuery.expr.match.needsContext, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + ret = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + cur = ret.push( cur ); + break; + } + } + } + + return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( jQuery.unique(all) ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.unique( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( isSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not; + }); +} +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rtbody = /\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] + }, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement("div") ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var + // Snapshot the DOM in case .domManip sweeps something relevant into its fragment + args = jQuery.map( this, function( elem ) { + return [ elem.nextSibling, elem.parentNode ]; + }), + i = 0; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + var next = args[ i++ ], + parent = args[ i++ ]; + + if ( parent ) { + // Don't use the snapshot next if it has moved (#13810) + if ( next && next.parentNode !== parent ) { + next = this.nextSibling; + } + jQuery( this ).remove(); + parent.insertBefore( elem, next ); + } + // Allow new content to include elements from the context set + }, true ); + + // Force removal if there was no new content (e.g., from empty arguments) + return i ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback, allowIntersection ) { + + // Flatten any nested arrays + args = core_concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[0], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[0] = value.call( this, index, self.html() ); + } + self.domManip( args, callback, allowIntersection ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[i], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Hope ajax is available... + jQuery._evalUrl( node.src ); + } else { + jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return this; + } +}); + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (jQuery.find.attr( elem, "type" ) !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[1]; + } else { + elem.removeAttribute("type"); + } + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; (elem = elems[i]) != null; i++ ) { + jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) ); + } +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone(true); + jQuery( insert[i] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + core_push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + +// Used in buildFragment, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( manipulation_rcheckableType.test( elem.type ) ) { + elem.defaultChecked = elem.checked; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; (node = srcElements[i]) != null; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + fixCloneNodeIssues( node, destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; (node = srcElements[i]) != null; i++ ) { + cloneCopyEvent( node, destElements[i] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var j, elem, contains, + tmp, tag, tbody, wrap, + l = elems.length, + + // Ensure a safe fragment + safe = createSafeFragment( context ), + + nodes = [], + i = 0; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || safe.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + + tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[2]; + + // Descend through wrappers to the right content + j = wrap[0]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Manually add leading whitespace removed by IE + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) ); + } + + // Remove IE's autoinserted from table fragments + if ( !jQuery.support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[1] === "
" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) { + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !jQuery.support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; + }, + + cleanData: function( elems, /* internal */ acceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + deleteExpando = jQuery.support.deleteExpando, + special = jQuery.event.special; + + for ( ; (elem = elems[i]) != null; i++ ) { + + if ( acceptData || jQuery.acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( deleteExpando ) { + delete elem[ internalKey ]; + + } else if ( typeof elem.removeAttribute !== core_strundefined ) { + elem.removeAttribute( internalKey ); + + } else { + elem[ internalKey ] = null; + } + + core_deletedIds.push( id ); + } + } + } + } + }, + + _evalUrl: function( url ) { + return jQuery.ajax({ + url: url, + type: "GET", + dataType: "script", + async: false, + global: false, + "throws": true + }); + } +}); +jQuery.fn.extend({ + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + } +}); +var iframe, getStyles, curCSS, + ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity\s*=\s*([^)]*)/, + rposition = /^(top|right|bottom|left)$/, + // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" + // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rmargin = /^margin/, + rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), + rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), + rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), + elemdisplay = { BODY: "block" }, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: 0, + fontWeight: 400 + }, + + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; + +// return a css property mapped to a potentially vendor prefixed property +function vendorPropName( style, name ) { + + // shortcut for names that are not vendor prefixed + if ( name in style ) { + return name; + } + + // check for vendor prefixed names + var capName = name.charAt(0).toUpperCase() + name.slice(1), + origName = name, + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in style ) { + return name; + } + } + + return origName; +} + +function isHidden( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); +} + +function showHide( elements, show ) { + var display, elem, hidden, + values = [], + index = 0, + length = elements.length; + + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + values[ index ] = jQuery._data( elem, "olddisplay" ); + display = elem.style.display; + if ( show ) { + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !values[ index ] && display === "none" ) { + elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( elem.style.display === "" && isHidden( elem ) ) { + values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); + } + } else { + + if ( !values[ index ] ) { + hidden = isHidden( elem ); + + if ( display && display !== "none" || !hidden ) { + jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) ); + } + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( index = 0; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + if ( !show || elem.style.display === "none" || elem.style.display === "" ) { + elem.style.display = show ? values[ index ] || "" : "none"; + } + } + + return elements; +} + +jQuery.fn.extend({ + css: function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + var len, styles, + map = {}, + i = 0; + + if ( jQuery.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + }, + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + var bool = typeof state === "boolean"; + + return this.each(function() { + if ( bool ? state : isHidden( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + }); + } +}); + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "columnCount": true, + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = jQuery.camelCase( name ), + style = elem.style; + + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // Fixes #8908, it can be done more correctly by specifing setters in cssHooks, + // but it would mean to define eight (for every problematic property) identical functions + if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) { + + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var num, val, hooks, + origName = jQuery.camelCase( name ); + + // Make sure that we're working with the right name + name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) ); + + // gets hook for the prefixed version + // followed by the unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + //convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Return, converting to number if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || jQuery.isNumeric( num ) ? num || 0 : val; + } + return val; + } +}); + +// NOTE: we've included the "window" in window.getComputedStyle +// because jsdom on node.js will break without it. +if ( window.getComputedStyle ) { + getStyles = function( elem ) { + return window.getComputedStyle( elem, null ); + }; + + curCSS = function( elem, name, _computed ) { + var width, minWidth, maxWidth, + computed = _computed || getStyles( elem ), + + // getPropertyValue is only needed for .css('filter') in IE9, see #12537 + ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined, + style = elem.style; + + if ( computed ) { + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right + // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels + // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret; + }; +} else if ( document.documentElement.currentStyle ) { + getStyles = function( elem ) { + return elem.currentStyle; + }; + + curCSS = function( elem, name, _computed ) { + var left, rs, rsLeft, + computed = _computed || getStyles( elem ), + ret = computed ? computed[ name ] : undefined, + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && style[ name ] ) { + ret = style[ name ]; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + // but not position css attributes, as those are proportional to the parent element instead + // and we can't measure the parent instead because it might trigger a "stacking dolls" problem + if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) { + + // Remember the original values + left = style.left; + rs = elem.runtimeStyle; + rsLeft = rs && rs.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + rs.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + rs.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +function setPositiveNumber( elem, value, subtract ) { + var matches = rnumsplit.exec( value ); + return matches ? + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) : + value; +} + +function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) { + var i = extra === ( isBorderBox ? "border" : "content" ) ? + // If we already have the right measurement, avoid augmentation + 4 : + // Otherwise initialize for horizontal or vertical properties + name === "width" ? 1 : 0, + + val = 0; + + for ( ; i < 4; i += 2 ) { + // both box models exclude margin, so add it if we want it + if ( extra === "margin" ) { + val += jQuery.css( elem, extra + cssExpand[ i ], true, styles ); + } + + if ( isBorderBox ) { + // border-box includes padding, so remove it if we want content + if ( extra === "content" ) { + val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // at this point, extra isn't border nor margin, so remove border + if ( extra !== "margin" ) { + val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } else { + // at this point, extra isn't content, so add padding + val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // at this point, extra isn't content nor padding, so add border + if ( extra !== "padding" ) { + val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + return val; +} + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property, which is equivalent to the border-box value + var valueIsBorderBox = true, + val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + styles = getStyles( elem ), + isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // some non-html elements return undefined for offsetWidth, so check for null/undefined + // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285 + // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668 + if ( val <= 0 || val == null ) { + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name, styles ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // we need the check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] ); + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + } + + // use the active box-sizing model to add/subtract irrelevant styles + return ( val + + augmentWidthOrHeight( + elem, + name, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles + ) + ) + "px"; +} + +// Try to determine the default display value of an element +function css_defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + // Use the already-created iframe if possible + iframe = ( iframe || + jQuery("