diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..54a484d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: node_js + +sudo: false + +install: + - "npm install mocha -g" + +before_script: + - "wget http://dl.node-webkit.org/live-build/09-06-2014/linux32_master-build-170-4beeddf-692f0ab-731b9e2-0f7c844-ae5bb86-86728d1/chromedriver-nw-v0.10.5-pre-linux-ia32.tar.gz" + - "wget http://dl.node-webkit.org/live-build/09-06-2014/linux32_master-build-170-4beeddf-692f0ab-731b9e2-0f7c844-ae5bb86-86728d1/node-webkit-v0.10.5-pre-linux-ia32.tar.gz" + - "unzip -j chromedriver-nw-v0.10.5-pre-osx-ia32.zip" + - "unzip -j node-webkit-v0.10.5-pre-linux-ia32.tar.gz" + - "sh -e /etc/init.d/xvfb start" + - sleep 5 # give xvfb some time to start + +script: + - "npm test" \ No newline at end of file diff --git a/app.js b/app.js index 680fb3c..336b4c7 100644 --- a/app.js +++ b/app.js @@ -239,8 +239,10 @@ $(document).on("ready",function() duplicateNote(current); }); - $("#note").on("tripleclick",{ threshold: 600 }, function() + $("#note").on("tripleclick",{ threshold: 600 }, function(e) { + + console.log($.nearest({x: e.clientX, y: e.clientY})); if (displayShowing()) { //Unselect text from doubleclick. diff --git a/index.html b/index.html index 313224f..46b55d6 100755 --- a/index.html +++ b/index.html @@ -17,6 +17,7 @@ + diff --git a/js/jquery.nearest.js b/js/jquery.nearest.js new file mode 100644 index 0000000..724e6c8 --- /dev/null +++ b/js/jquery.nearest.js @@ -0,0 +1,253 @@ +/*! + * jQuery Nearest plugin v1.3.0 + * + * Finds elements closest to a single point based on screen location and pixel dimensions + * http://gilmoreorless.github.io/jquery-nearest/ + * Open source under the MIT licence: http://gilmoreorless.mit-license.org/2011/ + * + * Requires jQuery 1.4 or above + * Also supports Ben Alman's "each2" plugin for faster looping (if available) + */ + +/** + * Method signatures: + * + * $.nearest({x, y}, selector) - find $(selector) closest to point + * $(elem).nearest(selector) - find $(selector) closest to elem + * $(elemSet).nearest({x, y}) - filter $(elemSet) and return closest to point + * + * Also: + * $.furthest() + * $(elem).furthest() + * + * $.touching() + * $(elem).touching() + */ +;(function ($, undefined) { + + /** + * Internal method that does the grunt work + * + * @param mixed selector Any valid jQuery selector providing elements to filter + * @param hash options Key/value list of options for matching elements + * @param mixed thisObj (optional) Any valid jQuery selector that represents self + * for the "includeSelf" option + * @return array List of matching elements, can be zero length + */ + var rPerc = /^([\d.]+)%$/; + function nearest(selector, options, thisObj) { + // Normalise selector and dimensions + selector || (selector = 'div'); // I STRONGLY recommend passing in a selector + var $container = $(options.container), + containerOffset = $container.offset() || {left: 0, top: 0}, + containerWH = [ + $container.width() || 0, + $container.height() || 0 + ], + containerProps = { + // prop: [min, max] + x: [containerOffset.left, containerOffset.left + containerWH[0]], + y: [containerOffset.top, containerOffset.top + containerWH[1]], + w: [0, containerWH[0]], + h: [0, containerWH[1]] + }, + prop, dims, match; + for (prop in containerProps) if (containerProps.hasOwnProperty(prop)) { + match = rPerc.exec(options[prop]); + if (match) { + dims = containerProps[prop]; + options[prop] = (dims[1] - dims[0]) * match[1] / 100 + dims[0]; + } + } + + // Deprecated options - remove in 2.0 + if (options.sameX === false && options.checkHoriz === false) { + options.sameX = !options.checkHoriz; + } + if (options.sameY === false && options.checkVert === false) { + options.sameY = !options.checkVert; + } + + // Get elements and work out x/y points + var $all = $container.find(selector), + cache = [], + furthest = !!options.furthest, + checkX = !options.sameX, + checkY = !options.sameY, + onlyX = !!options.onlyX, + onlyY = !!options.onlyY, + compDist = furthest ? 0 : Infinity, + point1x = parseFloat(options.x) || 0, + point1y = parseFloat(options.y) || 0, + point2x = parseFloat(point1x + options.w) || point1x, + point2y = parseFloat(point1y + options.h) || point1y, + tolerance = parseFloat(options.tolerance) || 0, + hasEach2 = !!$.fn.each2, + // Shortcuts to help with compression + min = Math.min, + max = Math.max; + + // Normalise the remaining options + if (!options.includeSelf && thisObj) { + $all = $all.not(thisObj); + } + if (tolerance < 0) { + tolerance = 0; + } + // Loop through all elements and check their positions + $all[hasEach2 ? 'each2' : 'each'](function (i, elem) { + var $this = hasEach2 ? elem : $(this), + off = $this.offset(), + x = off.left, + y = off.top, + w = $this.outerWidth(), + h = $this.outerHeight(), + x2 = x + w, + y2 = y + h, + maxX1 = max(x, point1x), + minX2 = min(x2, point2x), + maxY1 = max(y, point1y), + minY2 = min(y2, point2y), + intersectX = minX2 >= maxX1, + intersectY = minY2 >= maxY1, + distX, distY, distT, isValid; + if ( + // .nearest() / .furthest() + (checkX && checkY) || + // .touching() + (!checkX && !checkY && intersectX && intersectY) || + // .nearest({sameY: true}) + (checkX && intersectY) || + // .nearest({sameX: true}) + (checkY && intersectX) || + // .nearest({onlyX: true}) + (checkX && onlyX) || + // .nearest({onlyY: true}) + (checkY && onlyY) + ) { + distX = intersectX ? 0 : maxX1 - minX2; + distY = intersectY ? 0 : maxY1 - minY2; + if (onlyX || onlyY) { + distT = onlyX ? distX : distY; + } else { + distT = intersectX || intersectY ? + max(distX, distY) : + Math.sqrt(distX * distX + distY * distY); + } + isValid = furthest ? + distT >= compDist - tolerance : + distT <= compDist + tolerance; + if (isValid) { + compDist = furthest ? + max(compDist, distT) : + min(compDist, distT); + cache.push({ + node: this, + dist: distT + }); + } + } + }); + // Make sure all cached items are within tolerance range + var len = cache.length, + filtered = [], + compMin, compMax, + i, item; + if (len) { + if (furthest) { + compMin = compDist - tolerance; + compMax = compDist; + } else { + compMin = compDist; + compMax = compDist + tolerance; + } + for (i = 0; i < len; i++) { + item = cache[i]; + if (item.dist >= compMin && item.dist <= compMax) { + filtered.push(item.node); + } + } + } + return filtered; + } + + $.each(['nearest', 'furthest', 'touching'], function (i, name) { + + // Internal default options + // Not exposed publicly because they're method-dependent and easily overwritten anyway + var defaults = { + x: 0, // X position of top left corner of point/region + y: 0, // Y position of top left corner of point/region + w: 0, // Width of region + h: 0, // Height of region + tolerance: 1, // Distance tolerance in pixels, mainly to handle fractional pixel rounding bugs + container: document, // Container of objects for calculating %-based dimensions + furthest: name == 'furthest', // Find max distance (true) or min distance (false) + includeSelf: false, // Include 'this' in search results (t/f) - only applies to $(elem).func(selector) syntax + sameX: name === 'touching', // Only match for the same X axis values (t/f) + sameY: name === 'touching', // Only match for the same Y axis values (t/f) + onlyX: false, // Only check X axis variations (t/f) + onlyY: false // Only check Y axis variations (t/f) + }; + + /** + * $.nearest() / $.furthest() / $.touching() + * + * Utility functions for finding elements near a specific point or region on screen + * + * @param hash point Co-ordinates for the point or region to measure from + * "x" and "y" keys are required, "w" and "h" keys are optional + * @param mixed selector Any valid jQuery selector that provides elements to filter + * @param hash options (optional) Extra filtering options + * Not technically needed as the options could go on the point object, + * but it's good to have a consistent API + * @return jQuery object containing matching elements in selector + */ + $[name] = function (point, selector, options) { + if (!point || point.x === undefined || point.y === undefined) { + return $([]); + } + var opts = $.extend({}, defaults, point, options || {}); + return $(nearest(selector, opts)); + }; + + /** + * SIGNATURE 1: + * $(elem).nearest(selector) / $(elem).furthest(selector) / $(elem).touching(selector) + * + * Finds all elements in selector that are nearest to/furthest from elem + * + * @param mixed selector Any valid jQuery selector that provides elements to filter + * @param hash options (optional) Extra filtering options + * @return jQuery object containing matching elements in selector + * + * SIGNATURE 2: + * $(elemSet).nearest(point) / $(elemSet).furthest(point) / $(elemSet).touching(point) + * + * Filters elemSet to return only the elements nearest to/furthest from point + * Effectively a wrapper for $.nearest(point, elemSet) but with the benefits of method chaining + * + * @param hash point Co-ordinates for the point or region to measure from + * @return jQuery object containing matching elements in elemSet + */ + $.fn[name] = function (selector, options) { + if (!this.length) { + return this.pushStack([]); + } + var opts; + if (selector && $.isPlainObject(selector)) { + opts = $.extend({}, defaults, selector, options || {}); + return this.pushStack(nearest(this, opts)); + } + var offset = this.offset(), + dimensions = { + x: offset.left, + y: offset.top, + w: this.outerWidth(), + h: this.outerHeight() + }; + opts = $.extend({}, defaults, dimensions, options || {}); + return this.pushStack(nearest(selector, opts, this)); + }; + }); +})(jQuery); \ No newline at end of file diff --git a/tests/tests.js b/tests/tests.js index d4a2fa2..14fdf21 100644 --- a/tests/tests.js +++ b/tests/tests.js @@ -12,7 +12,9 @@ var notes2 = Array(); test.describe('Marknote', function() { - + this.timeout(50000); + test.describe('Notes', function() + { test.it('Has notes.', function() { driver.executeScript("return notes").then(function(arr) @@ -27,14 +29,82 @@ test.describe('Marknote', function() test.it('Can add note.', function() { + driver.wait(function() + { + return driver.isElementPresent(webdriver.By.id('newNote')); + }, 5000); driver.findElement(webdriver.By.id('newNote')).click(); driver.executeScript('return notes').then(function(arr) { notes2 = arr; - assert.equal(notes2.length, notes.length + 1); + assert.notEqual(notes2.length, notes.length); + }); + + + }); + + test.it('Can open editor.', function() + { + + driver.findElement(webdriver.By.id('display')).click(); + driver.findElement(webdriver.By.id('display')).click(); + driver.findElement(webdriver.By.id('display')).click(); + + driver.executeScript('return displayShowing()').then(function(test) + { + assert.equal(test, false); + }); + }); + + test.it('Can edit note.', function() + { + var pretext; + driver.executeScript('return editor.getValue();').then(function(pre) + { + pretext=pre; + }); + driver.executeScript("editor.insert('test');"); + driver.executeScript('return editor.getValue();').then(function(post) + { + assert.notEqual(pretext, post); + }); + }); + + + test.it('Can save note.', function() + { + driver.findElement(webdriver.By.id('note')).click(); + driver.findElement(webdriver.By.id('note')).click(); + driver.findElement(webdriver.By.id('note')).click(); + + driver.wait(function() + { + return driver.isElementPresent(webdriver.By.id('display')); + }, 1000); + + driver.executeScript('return displayShowing()').then(function(test) + { + assert.equal(test, true); + }); + + }); + + test.it('Can delete note.', function() + { + driver.executeScript('$("#actions").css("display", "block");').then(function() + { + driver.findElement(webdriver.By.xpath('//paper-icon-button[@icon="close"]')).click(); }); + driver.executeScript("return notes").then(function(arr) + { + notes = arr; + assert.notEqual(notes.length, notes2.length); + }); }); -}); \ No newline at end of file + +}); + +});