├── LICENSE ├── Rakefile ├── component.json ├── demos ├── img │ ├── fra.jpeg │ ├── fra2.jpeg │ ├── okshadow-example.png │ ├── okzoom.png │ └── photocopy.png ├── index.html ├── test_change.html ├── test_data.html ├── test_div.html ├── test_multi.html └── test_vanilla.html ├── img └── okzoom.png ├── okzoom.jquery.json ├── readme.textile ├── spec └── javascripts │ ├── helpers │ └── jasmine-jquery-1.3.1.js │ ├── okzoomSpec.js │ ├── support │ ├── jasmine.yml │ ├── jasmine_config.rb │ └── jasmine_runner.rb │ └── vendor │ └── jquery.js └── src ├── PREAMBLE ├── minify.sh ├── okzoom.js └── okzoom.min.js /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 OKFocus LLC. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | 2 | begin 3 | require 'jasmine' 4 | load 'jasmine/tasks/jasmine.rake' 5 | rescue LoadError 6 | task :jasmine do 7 | abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OKZoom", 3 | "version": "1.2.1", 4 | "main": "./src/okzoom.js", 5 | "dependencies": { 6 | "jquery": ">1.7.2" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demos/img/fra.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okfocus/okzoom/b324b70dbbd264be0293d3fef90829c2d80c0ae0/demos/img/fra.jpeg -------------------------------------------------------------------------------- /demos/img/fra2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okfocus/okzoom/b324b70dbbd264be0293d3fef90829c2d80c0ae0/demos/img/fra2.jpeg -------------------------------------------------------------------------------- /demos/img/okshadow-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okfocus/okzoom/b324b70dbbd264be0293d3fef90829c2d80c0ae0/demos/img/okshadow-example.png -------------------------------------------------------------------------------- /demos/img/okzoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okfocus/okzoom/b324b70dbbd264be0293d3fef90829c2d80c0ae0/demos/img/okzoom.png -------------------------------------------------------------------------------- /demos/img/photocopy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okfocus/okzoom/b324b70dbbd264be0293d3fef90829c2d80c0ae0/demos/img/photocopy.png -------------------------------------------------------------------------------- /demos/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OKZoom 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 30 | 31 | 32 | 50 | 175 | 189 | 190 | 191 | 192 |
193 | 210 | 212 |
213 |

OKZoom by OKFocus

214 |

215 | OKZoom is a JQuery plugin that produces a portable loupe of variable size and shape. 216 | All other jQuery 'zoom' plugins we have encountered implement a square magnifying area. 217 | Ours is a circle. You want a circle. 218 |

219 | 220 |

Usage

221 |

222 | Bind OKZoom to one or many image elements. The easiest way is to have a large image 223 | that is sized down using HTML or CSS: the loupe will show the full-size image on hover. 224 |

225 | 226 |
227 | 228 |
229 | 230 |
231 | $('img').okzoom({
232 |   width: 200,
233 |   height: 200,
234 |   round: true,
235 |   background: "#fff",
236 |   backgroundRepeat: "repeat",
237 |   shadow: "0 0 5px #000",
238 |   border: "1px solid black"
239 | });
240 | 241 |

242 | You can also apply OKZoom to every image on a page by binding to $('img'). 243 | If the source of the bound image changes -- say on an image gallery with several thumbnails 244 | under a main image -- the loupe will automatically update. 245 |

246 | 247 |

248 | Alternatively, add a HTML5 data attribute data-okimage to your image tag containing 249 | the url to the image you want to see inside the loupe. Using this technique, 250 | OKZoom may be applied to any element -- hover over this paragraph to see an example. 251 |

252 | 253 |

254 | Finally, the id of the loupe itself is ok-loupe. 255 |

256 | 257 |

Smoke Tests

258 | 259 |

260 | Use these basic tests to get started with OKZoom as quickly as possible. 261 |

262 | 263 | 270 | 271 |

Options

272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 |
widthof the loupe, in pixels150
heightof the loupe, in pixels150
scaleWidthoptionally force magnification of imagenull
roundround loupe if true, square if falsetrue
backgroundcolor for image off the edge of the loupe#fff
backgroundRepeatrepeat the image within the loupeno-repeat
borderborder around the loupe0
shadowbox-shadow on the loupe0 0 5px #000
306 | 307 |

Download OKZoom or Contribute on Github

308 | 309 | 310 |

311 |

License

312 |
313 | Copyright © 2012 OKFocus, http://okfoc.us
314 | 
315 | Permission is hereby granted, free of charge, to any person obtaining
316 | a copy of this software and associated documentation files (the
317 | “Software”), to deal in the Software without restriction, including
318 | without limitation the rights to use, copy, modify, merge, publish,
319 | distribute, sublicense, and/or sell copies of the Software, and to
320 | permit persons to whom the Software is furnished to do so, subject to
321 | the following conditions:
322 | 
323 | The above copyright notice and this permission notice shall be
324 | included in all copies or substantial portions of the Software.
325 | 
326 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
327 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
328 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
329 | NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
330 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
331 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
332 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
333 |
334 | 335 |
336 | 353 | 354 | 355 | -------------------------------------------------------------------------------- /demos/test_change.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OKZoom Change Image Test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 38 | 64 | 65 | 66 | 67 | 68 | 75 | 76 |
69 |
70 | 71 |
72 |

73 | 74 |
77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /demos/test_data.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OKZoom Data Attribute Test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 26 | 52 | 53 | 54 | 55 | 56 | 61 | 62 |
57 |
58 | 59 |
60 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /demos/test_div.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OKZoom Non-Image Test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 26 | 46 | 47 | 48 | 49 | 50 | 55 | 56 |
51 |
52 | Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. 53 |
54 |
57 | 58 | 59 | -------------------------------------------------------------------------------- /demos/test_multi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OKZoom Multi-Image Test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 26 | 44 | 45 | 46 | 47 | 48 | 52 | 53 |
49 |

50 | 51 |
54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /demos/test_vanilla.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OKZoom Basic Test 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 24 | 42 | 43 | 44 | 45 | 46 | 49 | 50 |
47 | 48 |
51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /img/okzoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/okfocus/okzoom/b324b70dbbd264be0293d3fef90829c2d80c0ae0/img/okzoom.png -------------------------------------------------------------------------------- /okzoom.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "okzoom", 3 | "version": "1.2.1", 4 | "title": "okzoom", 5 | "description": "A jQuery Plugin that produces a portable loupe of variable size and shape.", 6 | "homepage": "https://github.com/okfocus/okzoom", 7 | "docs": "https://github.com/okfocus/okzoom", 8 | "demo": "http://okfoc.us/okzoom", 9 | "licenses": [{ 10 | "type": "MIT", 11 | "url": "https://raw.github.com/okfocus/okzoom/master/license" 12 | }], 13 | "keywords": ["zoom", "magnifying", "magnifying glass", "circle", "image", "loupe", "okfocus"], 14 | "author": { 15 | "name": "OKFocus", 16 | "url": "https://github.com/okfocus" 17 | }, 18 | "maintainers": [{ 19 | "name": "Jules LaPlace", 20 | "url": "https://github.com/julescarbon" 21 | }, 22 | { 23 | "name": "Ryder Ripps", 24 | "url": "https://github.com/ryderr" 25 | }], 26 | "repository": { 27 | "type": "git", 28 | "url": "git://github.com/okfocus/okzoom.git" 29 | }, 30 | "bugs": "https://github.com/okfocus/okzoom/issues", 31 | "dependencies": { 32 | "jquery": ">=1.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /readme.textile: -------------------------------------------------------------------------------- 1 | h1. okzoom 2 | 3 | h2. OKZoom by OKFocus 4 | 5 | p. OKZoom is a jQuery plugin that produces a portable loupe of variable size and shape. All other jQuery 'zoom' plugins we have encountered implement a square magnifying area. Ours is a circle. You want a circle. See a demo "here":http://okfoc.us/okzoom. 6 | 7 | h2. Usage 8 | 9 | p. You can bind OKZoom to one or many image elements, producing a "zoom" effect like this: 10 | 11 | !https://github.com/okfocus/okzoom/raw/master/demos/img/okshadow-example.png! 12 | 13 | p. With code like this: 14 | 15 |
16 | 
17 | $('img').okzoom({
18 |   width: 200,
19 |   height: 200,
20 |   round: true,
21 |   background: "#fff",
22 |   backgroundRepeat: "repeat",
23 |   shadow: "0 0 5px #000",
24 |   border: "1px solid black"
25 | });
26 | 
27 | 
28 | 29 | h3. Options 30 | 31 | |Usage||| 32 | |width|(in pixels}|150| 33 | |height|(in pixels}|150| 34 | |scaleWidth|optionally resize the loupe image|null| 35 | |round|round loupe if true, square if false|true| 36 | |background|color for image off the edge of the loupe|#fff| 37 | |backgroundRepeat|repeat the image within the loupe|no-repeat| 38 | |border|border around the loupe|0| 39 | |shadow|box-shadow on the loupe|0 0 5px #000| 40 | 41 | h3. Data Attribute 42 | 43 | Typically, we use this plugin on an image that has been sized down in HTML, and the loupe displays the image at its normal size. If you like, you can add an HTML5 data attribute **data-okimage** to your image to substitute alternative content inside the loupe. 44 | 45 | h3. Run Tests 46 | 47 | p. OKFocus tests JavaScript with Jasmine. Run tests: 48 | 49 |
50 | 
51 | $ bundle install
52 | $ jasmine init
53 | $ rake jasmine
54 | 
55 | 
56 | -------------------------------------------------------------------------------- /spec/javascripts/helpers/jasmine-jquery-1.3.1.js: -------------------------------------------------------------------------------- 1 | 2 | var readFixtures = function() { 3 | return jasmine.getFixtures().proxyCallTo_('read', arguments); 4 | }; 5 | 6 | var preloadFixtures = function() { 7 | jasmine.getFixtures().proxyCallTo_('preload', arguments); 8 | }; 9 | 10 | var loadFixtures = function() { 11 | jasmine.getFixtures().proxyCallTo_('load', arguments); 12 | }; 13 | 14 | var setFixtures = function(html) { 15 | jasmine.getFixtures().set(html); 16 | }; 17 | 18 | var sandbox = function(attributes) { 19 | return jasmine.getFixtures().sandbox(attributes); 20 | }; 21 | 22 | var spyOnEvent = function(selector, eventName) { 23 | jasmine.JQuery.events.spyOn(selector, eventName); 24 | } 25 | 26 | jasmine.getFixtures = function() { 27 | return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures(); 28 | }; 29 | 30 | jasmine.Fixtures = function() { 31 | this.containerId = 'jasmine-fixtures'; 32 | this.fixturesCache_ = {}; 33 | this.fixturesPath = 'spec/javascripts/fixtures'; 34 | }; 35 | 36 | jasmine.Fixtures.prototype.set = function(html) { 37 | this.cleanUp(); 38 | this.createContainer_(html); 39 | }; 40 | 41 | jasmine.Fixtures.prototype.preload = function() { 42 | this.read.apply(this, arguments); 43 | }; 44 | 45 | jasmine.Fixtures.prototype.load = function() { 46 | this.cleanUp(); 47 | this.createContainer_(this.read.apply(this, arguments)); 48 | }; 49 | 50 | jasmine.Fixtures.prototype.read = function() { 51 | var htmlChunks = []; 52 | 53 | var fixtureUrls = arguments; 54 | for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) { 55 | htmlChunks.push(this.getFixtureHtml_(fixtureUrls[urlIndex])); 56 | } 57 | 58 | return htmlChunks.join(''); 59 | }; 60 | 61 | jasmine.Fixtures.prototype.clearCache = function() { 62 | this.fixturesCache_ = {}; 63 | }; 64 | 65 | jasmine.Fixtures.prototype.cleanUp = function() { 66 | jQuery('#' + this.containerId).remove(); 67 | }; 68 | 69 | jasmine.Fixtures.prototype.sandbox = function(attributes) { 70 | var attributesToSet = attributes || {}; 71 | return jQuery('
').attr(attributesToSet); 72 | }; 73 | 74 | jasmine.Fixtures.prototype.createContainer_ = function(html) { 75 | var container; 76 | if(html instanceof jQuery) { 77 | container = jQuery('
'); 78 | container.html(html); 79 | } else { 80 | container = '
' + html + '
' 81 | } 82 | jQuery('body').append(container); 83 | }; 84 | 85 | jasmine.Fixtures.prototype.getFixtureHtml_ = function(url) { 86 | if (typeof this.fixturesCache_[url] == 'undefined') { 87 | this.loadFixtureIntoCache_(url); 88 | } 89 | return this.fixturesCache_[url]; 90 | }; 91 | 92 | jasmine.Fixtures.prototype.loadFixtureIntoCache_ = function(relativeUrl) { 93 | var self = this; 94 | var url = this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl; 95 | jQuery.ajax({ 96 | async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded 97 | cache: false, 98 | dataType: 'html', 99 | url: url, 100 | success: function(data) { 101 | self.fixturesCache_[relativeUrl] = data; 102 | }, 103 | error: function(jqXHR, status, errorThrown) { 104 | throw Error('Fixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + errorThrown.message + ')'); 105 | } 106 | }); 107 | }; 108 | 109 | jasmine.Fixtures.prototype.proxyCallTo_ = function(methodName, passedArguments) { 110 | return this[methodName].apply(this, passedArguments); 111 | }; 112 | 113 | 114 | jasmine.JQuery = function() {}; 115 | 116 | jasmine.JQuery.browserTagCaseIndependentHtml = function(html) { 117 | return jQuery('
').append(html).html(); 118 | }; 119 | 120 | jasmine.JQuery.elementToString = function(element) { 121 | return jQuery('
').append(element.clone()).html(); 122 | }; 123 | 124 | jasmine.JQuery.matchersClass = {}; 125 | 126 | (function(namespace) { 127 | var data = { 128 | spiedEvents: {}, 129 | handlers: [] 130 | }; 131 | 132 | namespace.events = { 133 | spyOn: function(selector, eventName) { 134 | var handler = function(e) { 135 | data.spiedEvents[[selector, eventName]] = e; 136 | }; 137 | jQuery(selector).bind(eventName, handler); 138 | data.handlers.push(handler); 139 | }, 140 | 141 | wasTriggered: function(selector, eventName) { 142 | return !!(data.spiedEvents[[selector, eventName]]); 143 | }, 144 | 145 | cleanUp: function() { 146 | data.spiedEvents = {}; 147 | data.handlers = []; 148 | } 149 | } 150 | })(jasmine.JQuery); 151 | 152 | (function(){ 153 | var jQueryMatchers = { 154 | toHaveClass: function(className) { 155 | return this.actual.hasClass(className); 156 | }, 157 | 158 | toBeVisible: function() { 159 | return this.actual.is(':visible'); 160 | }, 161 | 162 | toBeHidden: function() { 163 | return this.actual.is(':hidden'); 164 | }, 165 | 166 | toBeSelected: function() { 167 | return this.actual.is(':selected'); 168 | }, 169 | 170 | toBeChecked: function() { 171 | return this.actual.is(':checked'); 172 | }, 173 | 174 | toBeEmpty: function() { 175 | return this.actual.is(':empty'); 176 | }, 177 | 178 | toExist: function() { 179 | return this.actual.size() > 0; 180 | }, 181 | 182 | toHaveAttr: function(attributeName, expectedAttributeValue) { 183 | return hasProperty(this.actual.attr(attributeName), expectedAttributeValue); 184 | }, 185 | 186 | toHaveId: function(id) { 187 | return this.actual.attr('id') == id; 188 | }, 189 | 190 | toHaveHtml: function(html) { 191 | return this.actual.html() == jasmine.JQuery.browserTagCaseIndependentHtml(html); 192 | }, 193 | 194 | toHaveText: function(text) { 195 | if (text && jQuery.isFunction(text.test)) { 196 | return text.test(this.actual.text()); 197 | } else { 198 | return this.actual.text() == text; 199 | } 200 | }, 201 | 202 | toHaveValue: function(value) { 203 | return this.actual.val() == value; 204 | }, 205 | 206 | toHaveData: function(key, expectedValue) { 207 | return hasProperty(this.actual.data(key), expectedValue); 208 | }, 209 | 210 | toBe: function(selector) { 211 | return this.actual.is(selector); 212 | }, 213 | 214 | toContain: function(selector) { 215 | return this.actual.find(selector).size() > 0; 216 | }, 217 | 218 | toBeDisabled: function(selector){ 219 | return this.actual.is(':disabled'); 220 | }, 221 | 222 | // tests the existence of a specific event binding 223 | toHandle: function(eventName) { 224 | var events = this.actual.data("events"); 225 | return events && events[eventName].length > 0; 226 | }, 227 | 228 | // tests the existence of a specific event binding + handler 229 | toHandleWith: function(eventName, eventHandler) { 230 | var stack = this.actual.data("events")[eventName]; 231 | var i; 232 | for (i = 0; i < stack.length; i++) { 233 | if (stack[i].handler == eventHandler) { 234 | return true; 235 | } 236 | } 237 | return false; 238 | } 239 | }; 240 | 241 | var hasProperty = function(actualValue, expectedValue) { 242 | if (expectedValue === undefined) { 243 | return actualValue !== undefined; 244 | } 245 | return actualValue == expectedValue; 246 | }; 247 | 248 | var bindMatcher = function(methodName) { 249 | var builtInMatcher = jasmine.Matchers.prototype[methodName]; 250 | 251 | jasmine.JQuery.matchersClass[methodName] = function() { 252 | if (this.actual instanceof jQuery) { 253 | var result = jQueryMatchers[methodName].apply(this, arguments); 254 | this.actual = jasmine.JQuery.elementToString(this.actual); 255 | return result; 256 | } 257 | 258 | if (builtInMatcher) { 259 | return builtInMatcher.apply(this, arguments); 260 | } 261 | 262 | return false; 263 | }; 264 | }; 265 | 266 | for(var methodName in jQueryMatchers) { 267 | bindMatcher(methodName); 268 | } 269 | })(); 270 | 271 | beforeEach(function() { 272 | this.addMatchers(jasmine.JQuery.matchersClass); 273 | this.addMatchers({ 274 | toHaveBeenTriggeredOn: function(selector) { 275 | this.message = function() { 276 | return [ 277 | "Expected event " + this.actual + " to have been triggered on" + selector, 278 | "Expected event " + this.actual + " not to have been triggered on" + selector 279 | ]; 280 | }; 281 | return jasmine.JQuery.events.wasTriggered(selector, this.actual); 282 | } 283 | }) 284 | }); 285 | 286 | afterEach(function() { 287 | jasmine.getFixtures().cleanUp(); 288 | jasmine.JQuery.events.cleanUp(); 289 | }); 290 | -------------------------------------------------------------------------------- /spec/javascripts/okzoomSpec.js: -------------------------------------------------------------------------------- 1 | describe('okzoom', function() { 2 | var img, loupe, 3 | imageUri = 'http://okfoc.us/assets/images/logo.gif', 4 | altImageUri = 'http://okfoc.us/assets/images/ok_icon.png'; 5 | 6 | beforeEach(function(){ 7 | jasmine.getFixtures().set(''); 8 | this.addMatchers({ 9 | toHaveBackgroundImage: function () { 10 | return /^url/.test(this.actual.css('backgroundImage')); 11 | }, 12 | toBeVisible: function () { 13 | return this.actual.css('display') === "block"; 14 | }, 15 | toBeHidden: function () { 16 | return this.actual.css('display') === "none"; 17 | }, 18 | }); 19 | }); 20 | 21 | describe('with default options', function () { 22 | beforeEach(function(){ 23 | img = $('img').okzoom(); 24 | loupe = $('#ok-loupe'); 25 | expect(loupe).toExist(); 26 | }); 27 | 28 | it('mouseover event gets loupe ready', function () { 29 | $('img').trigger('mouseover'); 30 | expect(loupe).toHaveBackgroundImage(); 31 | }); 32 | 33 | it('mousemove event displays loupe', function () { 34 | $('img').trigger('mouseover'); 35 | $('img').trigger('mousemove'); 36 | expect(loupe).toBeVisible(); 37 | }); 38 | 39 | it('mouseout hides loupe', function () { 40 | $('img').trigger('mouseover'); 41 | $('img').trigger('mousemove'); 42 | $('img').trigger('mouseout'); 43 | expect(loupe).toBeHidden(); 44 | expect(loupe.css('backgroundImage')).toBe('none'); 45 | }); 46 | }); 47 | 48 | describe('with data attribute set', function () { 49 | beforeEach(function(){ 50 | img = $('img').data('okimage', altImageUri).okzoom(); 51 | loupe = $('#ok-loupe'); 52 | expect(loupe).toExist(); 53 | }); 54 | 55 | it('mouseover event gets loupe ready', function () { 56 | $('img').trigger('mouseover'); 57 | expect(loupe).toHaveBackgroundImage(); 58 | expect(loupe.css('backgroundImage')).toBe('url(' + altImageUri + ')'); 59 | $('img').trigger('mouseout'); 60 | }); 61 | }); 62 | 63 | describe('with image src change', function () { 64 | beforeEach(function(){ 65 | img = $('img').okzoom(); 66 | loupe = $('#ok-loupe'); 67 | expect(loupe).toExist(); 68 | }); 69 | 70 | it('changing image src updates loupe', function () { 71 | $('img').trigger('mouseover'); 72 | $('img').trigger('mousemove'); 73 | expect(loupe.css('backgroundImage')).toBe('url(' + imageUri + ')'); 74 | $('img').trigger('mouseout'); 75 | $('img').attr('src', altImageUri); 76 | $('img').trigger('mouseover'); 77 | expect(loupe.css('backgroundImage')).toBe('url(' + altImageUri + ')'); 78 | $('img').trigger('mouseout'); 79 | }); 80 | 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine.yml: -------------------------------------------------------------------------------- 1 | # src_files 2 | # 3 | # Return an array of filepaths relative to src_dir to include before jasmine specs. 4 | # Default: [] 5 | # 6 | # EXAMPLE: 7 | # 8 | # src_files: 9 | # - lib/source1.js 10 | # - lib/source2.js 11 | # - dist/**/*.js 12 | # 13 | src_files: 14 | - spec/javascripts/vendor/jquery.js 15 | - src/okzoom-1.0.js 16 | # - src/*.min.js 17 | 18 | # stylesheets 19 | # 20 | # Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs. 21 | # Default: [] 22 | # 23 | # EXAMPLE: 24 | # 25 | # stylesheets: 26 | # - css/style.css 27 | # - stylesheets/*.css 28 | # 29 | stylesheets: 30 | 31 | # helpers 32 | # 33 | # Return an array of filepaths relative to spec_dir to include before jasmine specs. 34 | # Default: ["helpers/**/*.js"] 35 | # 36 | # EXAMPLE: 37 | # 38 | # helpers: 39 | # - helpers/**/*.js 40 | # 41 | helpers: 42 | 43 | # spec_files 44 | # 45 | # Return an array of filepaths relative to spec_dir to include. 46 | # Default: ["**/*[sS]pec.js"] 47 | # 48 | # EXAMPLE: 49 | # 50 | # spec_files: 51 | # - **/*[sS]pec.js 52 | # 53 | spec_files: 54 | 55 | # src_dir 56 | # 57 | # Source directory path. Your src_files must be returned relative to this path. Will use root if left blank. 58 | # Default: project root 59 | # 60 | # EXAMPLE: 61 | # 62 | # src_dir: public 63 | # 64 | src_dir: 65 | 66 | # spec_dir 67 | # 68 | # Spec directory path. Your spec_files must be returned relative to this path. 69 | # Default: spec/javascripts 70 | # 71 | # EXAMPLE: 72 | # 73 | # spec_dir: spec/javascripts 74 | # 75 | spec_dir: 76 | -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine_config.rb: -------------------------------------------------------------------------------- 1 | module Jasmine 2 | class Config 3 | 4 | # Add your overrides or custom config code here 5 | 6 | end 7 | end 8 | 9 | 10 | # Note - this is necessary for rspec2, which has removed the backtrace 11 | module Jasmine 12 | class SpecBuilder 13 | def declare_spec(parent, spec) 14 | me = self 15 | example_name = spec["name"] 16 | @spec_ids << spec["id"] 17 | backtrace = @example_locations[parent.description + " " + example_name] 18 | parent.it example_name, {} do 19 | me.report_spec(spec["id"]) 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine_runner.rb: -------------------------------------------------------------------------------- 1 | $:.unshift(ENV['JASMINE_GEM_PATH']) if ENV['JASMINE_GEM_PATH'] # for gem testing purposes 2 | 3 | require 'rubygems' 4 | require 'jasmine' 5 | jasmine_config_overrides = File.expand_path(File.join(File.dirname(__FILE__), 'jasmine_config.rb')) 6 | require jasmine_config_overrides if File.exist?(jasmine_config_overrides) 7 | if Jasmine::Dependencies.rspec2? 8 | require 'rspec' 9 | else 10 | require 'spec' 11 | end 12 | 13 | jasmine_config = Jasmine::Config.new 14 | spec_builder = Jasmine::SpecBuilder.new(jasmine_config) 15 | 16 | should_stop = false 17 | 18 | if Jasmine::Dependencies.rspec2? 19 | RSpec.configuration.after(:suite) do 20 | spec_builder.stop if should_stop 21 | end 22 | else 23 | Spec::Runner.configure do |config| 24 | config.after(:suite) do 25 | spec_builder.stop if should_stop 26 | end 27 | end 28 | end 29 | 30 | spec_builder.start 31 | should_stop = true 32 | spec_builder.declare_suites 33 | -------------------------------------------------------------------------------- /src/PREAMBLE: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | OKZoom by OKFocus v1.2 4 | http://okfoc.us // @okfocus 5 | Copyright 2012 OKFocus 6 | Licensed under the MIT License 7 | 8 | */ 9 | 10 | -------------------------------------------------------------------------------- /src/minify.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # OKZoom by OKFocus 4 | # http://okfoc.us // @okfocus 5 | # Copyright 2012 OKFocus 6 | # Licensed under the MIT License 7 | # 8 | # Minify using uglify 9 | 10 | uglify -s okzoom.js -o okzoom.ugly.js 11 | cat PREAMBLE okzoom.ugly.js > okzoom.min.js 12 | rm okzoom.ugly.js 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/okzoom.js: -------------------------------------------------------------------------------- 1 | /* 2 | * OKZoom by OKFocus v1.2 3 | * http://okfoc.us // @okfocus 4 | * Copyright 2012 OKFocus 5 | * Licensed under the MIT License 6 | **/ 7 | 8 | $(function($){ 9 | 10 | // Identify browser based on useragent string 11 | var browser = (function( ua ) { 12 | ua = ua.toLowerCase(); 13 | var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) || 14 | /(webkit)[ \/]([\w.]+)/.exec( ua ) || 15 | /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) || 16 | /(msie) ([\w.]+)/.exec( ua ) || 17 | ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) || 18 | []; 19 | var matched = { 20 | browser: match[ 1 ] || "", 21 | version: match[ 2 ] || "0" 22 | }; 23 | browser = {}; 24 | if ( matched.browser ) { 25 | browser[ matched.browser ] = true; 26 | browser.version = matched.version; 27 | } 28 | // Chrome is Webkit, but Webkit is also Safari. 29 | if ( browser.chrome ) { 30 | browser.webkit = true; 31 | } else if ( browser.webkit ) { 32 | browser.safari = true; 33 | } 34 | if (window.$) $.browser = browser; 35 | return browser; 36 | })( navigator.userAgent ); 37 | 38 | var is_iphone = (navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) 39 | var is_ipad = (navigator.userAgent.match(/iPad/i)) 40 | var is_android = (navigator.userAgent.match(/Android/i)) 41 | var is_mobile = is_iphone || is_ipad || is_android 42 | var is_desktop = ! is_mobile; 43 | var transitionProp = browser.safari ? "WebkitTransition" : "transition"; 44 | var transformProp = browser.safari ? "WebkitTransform" : "transform"; 45 | var longTransformProp = browser.safari ? "-webkit-transform" : "transform"; 46 | var transformOriginProp = browser.safari ? "WebkitTransformOrigin" : "transformOrigin"; 47 | 48 | $.fn.okzoom = function(options){ 49 | options = $.extend({}, $.fn.okzoom.defaults, options); 50 | 51 | return this.each(function(){ 52 | var base = {}; 53 | var el = this; 54 | base.options = options; 55 | base.$el = $(el); 56 | base.el = el; 57 | 58 | base.listener = document.createElement('div'); 59 | base.$listener = $(base.listener).addClass('ok-listener').css({ 60 | position: 'absolute', 61 | zIndex: 10000 62 | }); 63 | $('body').append(base.$listener); 64 | 65 | var loupe = document.createElement("div"); 66 | loupe.id = "ok-loupe"; 67 | loupe.style.position = "absolute"; 68 | loupe.style.backgroundRepeat = "no-repeat"; 69 | loupe.style.pointerEvents = "none"; 70 | loupe.style.opacity = 0; 71 | loupe.style.zIndex = 7879; 72 | $('body').append(loupe); 73 | base.loupe = loupe; 74 | 75 | base.$el.data("okzoom", base); 76 | 77 | base.options = options; 78 | 79 | if (is_mobile) { 80 | base.$el.bind('touchstart', (function(b) { 81 | return function(e) { 82 | console.log("TS", e) 83 | e.preventDefault() 84 | $.fn.okzoom.build(b, e.originalEvent.touches[0]); 85 | }; 86 | }(base))); 87 | 88 | base.$el.bind('touchmove', (function(b) { 89 | return function(e) { 90 | console.log("TM") 91 | e.preventDefault() 92 | $.fn.okzoom.mousemove(b, e.originalEvent.touches[0]); 93 | }; 94 | }(base))); 95 | 96 | base.$el.bind('touchend', (function(b) { 97 | return function(e) { 98 | console.log("TE") 99 | e.preventDefault() 100 | $.fn.okzoom.mouseout(b, e); 101 | }; 102 | }(base))); 103 | } 104 | else { 105 | $(base.el).bind('mouseover', (function(b) { 106 | return function(e) { $.fn.okzoom.build(b, e); }; 107 | }(base))); 108 | 109 | base.$listener.bind('mousemove', (function(b) { 110 | return function(e) { $.fn.okzoom.mousemove(b, e); }; 111 | }(base))); 112 | 113 | base.$listener.bind('mouseout', (function(b) { 114 | return function(e) { $.fn.okzoom.mouseout(b, e); }; 115 | }(base))); 116 | } 117 | 118 | base.options.height = base.options.height || base.options.width; 119 | 120 | base.image_from_data = base.$el.data("okimage"); 121 | base.has_data_image = typeof base.image_from_data !== "undefined"; 122 | base.timeout = null 123 | 124 | if (base.has_data_image) { 125 | base.img = new Image (); 126 | base.img.src = base.image_from_data; 127 | } 128 | 129 | base.msie = -1; // Return value assumes failure. 130 | if (navigator.appName == 'Microsoft Internet Explorer') { 131 | var ua = navigator.userAgent; 132 | var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); 133 | if (re.exec(ua) != null) 134 | base.msie = parseFloat(RegExp.$1); 135 | } 136 | }); 137 | }; 138 | 139 | $.fn.okzoom.defaults = { 140 | "width": 150, 141 | "height": null, 142 | "scaleWidth": null, 143 | "round": true, 144 | "background": "#fff", 145 | "backgroundRepeat": "no-repeat", 146 | "shadow": "0 0 5px #000", 147 | "inset": 0, 148 | "border": 0, 149 | "transform": is_mobile ? ["scale(0)","scale(1)"] : null, 150 | "transformOrigin": is_mobile ? "50% 100%" : "50% 50%", 151 | "transitionTime": 200, 152 | "transitionTimingFunction": "cubic-bezier(0,0,0,1)", 153 | }; 154 | 155 | $.fn.okzoom.build = function(base, e){ 156 | if (! base.has_data_image) { 157 | base.img = base.el; 158 | } 159 | else if (base.image_from_data != base.$el.attr('data-okimage')) { 160 | // data() returns cached values, whereas attr() returns from the dom. 161 | base.image_from_data = base.$el.attr('data-okimage'); 162 | 163 | $(base.img).remove(); 164 | base.img = new Image(); 165 | base.img.src = base.image_from_data; 166 | } 167 | 168 | if (base.msie > -1 && base.msie < 9.0 && !base.img.naturalized) { 169 | var naturalize = function(img) { 170 | img = img || this; 171 | var io = new Image(); 172 | 173 | io.el = img; 174 | io.src = img.src; 175 | 176 | img.naturalWidth = io.width; 177 | img.naturalHeight = io.height; 178 | img.naturalized = true; 179 | }; 180 | if (base.img.complete) naturalize(base.img); 181 | else return; 182 | } 183 | 184 | base.offset = base.$el.offset(); 185 | base.width = base.$el.width(); 186 | base.height = base.$el.height(); 187 | base.$listener.css({ 188 | display: 'block', 189 | width: base.$el.outerWidth(), 190 | height: base.$el.outerHeight(), 191 | top: base.$el.offset().top, 192 | left: base.$el.offset().left 193 | }); 194 | 195 | if (base.options.scaleWidth) { 196 | base.naturalWidth = base.options.scaleWidth; 197 | base.naturalHeight = Math.round( base.img.naturalHeight * base.options.scaleWidth / base.img.naturalWidth ); 198 | } else { 199 | base.naturalWidth = base.img.naturalWidth; 200 | base.naturalHeight = base.img.naturalHeight; 201 | } 202 | 203 | base.widthRatio = base.naturalWidth / base.width; 204 | base.heightRatio = base.naturalHeight / base.height; 205 | 206 | base.loupe.style.width = base.options.width + "px"; 207 | base.loupe.style.height = base.options.height + "px"; 208 | base.loupe.style.border = base.options.border; 209 | base.loupe.style.background = base.options.background + " url(" + base.img.src + ")"; 210 | base.loupe.style.backgroundRepeat = base.options.backgroundRepeat; 211 | base.loupe.style.backgroundSize = base.options.scaleWidth ? 212 | base.naturalWidth + "px " + base.naturalHeight + "px" : "auto"; 213 | base.loupe.style.borderRadius = 214 | base.loupe.style.MozBorderRadius = 215 | base.loupe.style.WebkitBorderRadius = base.options.round ? "50%" : 0; 216 | base.loupe.style.boxShadow = base.options.shadow; 217 | base.loupe.style.opacity = 0; 218 | if (base.options.transform) { 219 | base.loupe.style[transformProp] = base.options.transform[0] 220 | base.loupe.style[transformOriginProp] = base.options.transformOrigin 221 | base.loupe.style[transitionProp] = longTransformProp + " " + base.options.transitionTime 222 | } 223 | base.initialized = true; 224 | $.fn.okzoom.mousemove(base, e); 225 | }; 226 | 227 | $.fn.okzoom.mousemove = function (base, e) { 228 | if (!base.initialized) return; 229 | var shimLeft = base.options.width / 2; 230 | var shimTop = base.options.height / 2; 231 | var offsetTop = is_mobile ? base.options.height : shimTop 232 | var pageX = typeof e.pageX !== 'undefined' ? e.pageX : 233 | (e.clientX + document.documentElement.scrollLeft); 234 | var pageY = typeof e.pageY !== 'undefined' ? e.pageY : 235 | (e.clientY + document.documentElement.scrollTop); 236 | var scaleLeft = -1 * Math.floor( (pageX - base.offset.left) * base.widthRatio - shimLeft ); 237 | var scaleTop = -1 * Math.floor( (pageY - base.offset.top) * base.heightRatio - shimTop ); 238 | 239 | document.body.style.cursor = "none"; 240 | // base.loupe.style.display = "block"; 241 | base.loupe.style.left = pageX - shimLeft + "px"; 242 | base.loupe.style.top = pageY - offsetTop + "px"; 243 | base.loupe.style.backgroundPosition = scaleLeft + "px " + scaleTop + "px"; 244 | base.loupe.style.opacity = 1; 245 | if (base.options.transform) { 246 | base.loupe.style[transformProp] = base.options.transform[1] 247 | base.loupe.style[transformProp] = base.options.transform[1] 248 | base.loupe.style[transitionProp] = longTransformProp + " " + base.options.transitionTime + "ms " + base.options.transitionTimingFunction 249 | } 250 | clearTimeout(base.timeout) 251 | }; 252 | 253 | $.fn.okzoom.mouseout = function (base, e) { 254 | // base.loupe.style.display = "none"; 255 | if (base.options.transform) { 256 | base.loupe.style[transformProp] = base.options.transform[0] 257 | base.timeout = setTimeout(function(){ 258 | base.loupe.style.opacity = 0; 259 | }, base.options.transitionTime); 260 | } 261 | else { 262 | base.loupe.style.opacity = 0; 263 | } 264 | base.loupe.style.background = "none"; 265 | base.listener.style.display = "none"; 266 | document.body.style.cursor = "auto"; 267 | }; 268 | 269 | }); 270 | -------------------------------------------------------------------------------- /src/okzoom.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | OKZoom by OKFocus v1.2 4 | http://okfoc.us // @okfocus 5 | Copyright 2012 OKFocus 6 | Licensed under the MIT License 7 | 8 | */ 9 | 10 | $(function(a){var b=function(c){c=c.toLowerCase();var d=/(chrome)[ \/]([\w.]+)/.exec(c)||/(webkit)[ \/]([\w.]+)/.exec(c)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(c)||/(msie) ([\w.]+)/.exec(c)||c.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(c)||[],e={browser:d[1]||"",version:d[2]||"0"};return b={},e.browser&&(b[e.browser]=!0,b.version=e.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),window.$&&(a.browser=b),b}(navigator.userAgent),c=navigator.userAgent.match(/iPhone/i)||navigator.userAgent.match(/iPod/i),d=navigator.userAgent.match(/iPad/i),e=navigator.userAgent.match(/Android/i),f=c||d||e,g=b.safari?"WebkitTransition":"transition",h=b.safari?"WebkitTransform":"transform",i=b.safari?"-webkit-transform":"transform",j=b.safari?"WebkitTransformOrigin":"transformOrigin";a.fn.okzoom=function(b){return b=a.extend({},a.fn.okzoom.defaults,b),this.each(function(){var c={},d=this;c.options=b,c.$el=a(d),c.el=d,c.listener=document.createElement("div"),c.$listener=a(c.listener).addClass("ok-listener").css({position:"absolute",zIndex:1e4}),a("body").append(c.$listener);var e=document.createElement("div");if(e.id="ok-loupe",e.style.position="absolute",e.style.backgroundRepeat="no-repeat",e.style.pointerEvents="none",e.style.opacity=0,e.style.zIndex=7879,a("body").append(e),c.loupe=e,c.$el.data("okzoom",c),c.options=b,f?(c.$el.bind("touchstart",function(b){return function(c){console.log("TS",c),c.preventDefault(),a.fn.okzoom.build(b,c.originalEvent.touches[0])}}(c)),c.$el.bind("touchmove",function(b){return function(c){console.log("TM"),c.preventDefault(),a.fn.okzoom.mousemove(b,c.originalEvent.touches[0])}}(c)),c.$el.bind("touchend",function(b){return function(c){console.log("TE"),c.preventDefault(),a.fn.okzoom.mouseout(b,c)}}(c))):(a(c.el).bind("mouseover",function(b){return function(c){a.fn.okzoom.build(b,c)}}(c)),c.$listener.bind("mousemove",function(b){return function(c){a.fn.okzoom.mousemove(b,c)}}(c)),c.$listener.bind("mouseout",function(b){return function(c){a.fn.okzoom.mouseout(b,c)}}(c))),c.options.height=c.options.height||c.options.width,c.image_from_data=c.$el.data("okimage"),c.has_data_image="undefined"!=typeof c.image_from_data,c.timeout=null,c.has_data_image&&(c.img=new Image,c.img.src=c.image_from_data),c.msie=-1,"Microsoft Internet Explorer"==navigator.appName){var g=navigator.userAgent,h=new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})");null!=h.exec(g)&&(c.msie=parseFloat(RegExp.$1))}})},a.fn.okzoom.defaults={width:150,height:null,scaleWidth:null,round:!0,background:"#fff",backgroundRepeat:"no-repeat",shadow:"0 0 5px #000",inset:0,border:0,transform:f?["scale(0)","scale(1)"]:null,transformOrigin:f?"50% 100%":"50% 50%",transitionTime:200,transitionTimingFunction:"cubic-bezier(0,0,0,1)"},a.fn.okzoom.build=function(b,c){if(b.has_data_image?b.image_from_data!=b.$el.attr("data-okimage")&&(b.image_from_data=b.$el.attr("data-okimage"),a(b.img).remove(),b.img=new Image,b.img.src=b.image_from_data):b.img=b.el,b.msie>-1&&b.msie<9&&!b.img.naturalized){var d=function(a){a=a||this;var b=new Image;b.el=a,b.src=a.src,a.naturalWidth=b.width,a.naturalHeight=b.height,a.naturalized=!0};if(!b.img.complete)return;d(b.img)}b.offset=b.$el.offset(),b.width=b.$el.width(),b.height=b.$el.height(),b.$listener.css({display:"block",width:b.$el.outerWidth(),height:b.$el.outerHeight(),top:b.$el.offset().top,left:b.$el.offset().left}),b.options.scaleWidth?(b.naturalWidth=b.options.scaleWidth,b.naturalHeight=Math.round(b.img.naturalHeight*b.options.scaleWidth/b.img.naturalWidth)):(b.naturalWidth=b.img.naturalWidth,b.naturalHeight=b.img.naturalHeight),b.widthRatio=b.naturalWidth/b.width,b.heightRatio=b.naturalHeight/b.height,b.loupe.style.width=b.options.width+"px",b.loupe.style.height=b.options.height+"px",b.loupe.style.border=b.options.border,b.loupe.style.background=b.options.background+" url("+b.img.src+")",b.loupe.style.backgroundRepeat=b.options.backgroundRepeat,b.loupe.style.backgroundSize=b.options.scaleWidth?b.naturalWidth+"px "+b.naturalHeight+"px":"auto",b.loupe.style.borderRadius=b.loupe.style.MozBorderRadius=b.loupe.style.WebkitBorderRadius=b.options.round?"50%":0,b.loupe.style.boxShadow=b.options.shadow,b.loupe.style.opacity=0,b.options.transform&&(b.loupe.style[h]=b.options.transform[0],b.loupe.style[j]=b.options.transformOrigin,b.loupe.style[g]=i+" "+b.options.transitionTime),b.initialized=!0,a.fn.okzoom.mousemove(b,c)},a.fn.okzoom.mousemove=function(a,b){if(a.initialized){var c=a.options.width/2,d=a.options.height/2,e=f?a.options.height:d,j="undefined"!=typeof b.pageX?b.pageX:b.clientX+document.documentElement.scrollLeft,k="undefined"!=typeof b.pageY?b.pageY:b.clientY+document.documentElement.scrollTop,l=-1*Math.floor((j-a.offset.left)*a.widthRatio-c),m=-1*Math.floor((k-a.offset.top)*a.heightRatio-d);document.body.style.cursor="none",a.loupe.style.left=j-c+"px",a.loupe.style.top=k-e+"px",a.loupe.style.backgroundPosition=l+"px "+m+"px",a.loupe.style.opacity=1,a.options.transform&&(a.loupe.style[h]=a.options.transform[1],a.loupe.style[h]=a.options.transform[1],a.loupe.style[g]=i+" "+a.options.transitionTime+"ms "+a.options.transitionTimingFunction),clearTimeout(a.timeout)}},a.fn.okzoom.mouseout=function(a){a.options.transform?(a.loupe.style[h]=a.options.transform[0],a.timeout=setTimeout(function(){a.loupe.style.opacity=0},a.options.transitionTime)):a.loupe.style.opacity=0,a.loupe.style.background="none",a.listener.style.display="none",document.body.style.cursor="auto"}}); --------------------------------------------------------------------------------