├── 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 |
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 | width
274 | of the loupe, in pixels
275 | 150
276 |
277 | height
278 | of the loupe, in pixels
279 | 150
280 |
281 | scaleWidth
282 | optionally force magnification of image
283 | null
284 |
285 | round
286 | round loupe if true, square if false
287 | true
288 |
289 | background
290 | color for image off the edge of the loupe
291 | #fff
292 |
293 | backgroundRepeat
294 | repeat the image within the loupe
295 | no-repeat
296 |
297 | border
298 | border around the loupe
299 | 0
300 |
301 | shadow
302 | box-shadow on the loupe
303 | 0 0 5px #000
304 |
305 |
306 |
307 |
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 |
69 |
70 |
71 |
72 |
73 | Toggle Image
74 |
75 |
76 |
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 |
57 |
58 |
59 |
60 |
61 |
62 |
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 |
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 |
55 |
56 |
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 |
49 |
50 |
51 |
52 |
53 |
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 |
47 |
48 |
49 |
50 |
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"}});
--------------------------------------------------------------------------------