├── src
├── index.css
└── index.js
├── .gitignore
├── .gitattributes
├── docs
├── images
│ ├── tibet-1.jpg
│ ├── tibet-2.jpg
│ ├── tibet-3.jpg
│ ├── tibet-4.jpg
│ ├── tibet-5.jpg
│ ├── tibet-6.jpg
│ ├── tibet-7.jpg
│ ├── tibet-8.jpg
│ ├── tibet-9.jpg
│ └── thumbnails
│ │ ├── tibet-1.jpg
│ │ ├── tibet-2.jpg
│ │ ├── tibet-3.jpg
│ │ ├── tibet-4.jpg
│ │ ├── tibet-5.jpg
│ │ ├── tibet-6.jpg
│ │ ├── tibet-7.jpg
│ │ ├── tibet-8.jpg
│ │ └── tibet-9.jpg
├── css
│ ├── main.css
│ └── viewer.css
├── js
│ └── main.js
└── index.html
├── .travis.yml
├── .editorconfig
├── .stylelintrc
├── babel.config.js
├── postcss.config.js
├── .eslintrc
├── LICENSE
├── karma.conf.js
├── rollup.config.js
├── test
└── index.js
├── CHANGELOG.md
├── README.md
├── package.json
└── dist
├── viewer.min.css
├── viewer.css
├── viewer.min.js
└── viewer.esm.js
/src/index.css:
--------------------------------------------------------------------------------
1 | @import 'viewerjs/src';
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.local
2 | *.map
3 | coverage
4 | node_modules
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/docs/images/tibet-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/tibet-1.jpg
--------------------------------------------------------------------------------
/docs/images/tibet-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/tibet-2.jpg
--------------------------------------------------------------------------------
/docs/images/tibet-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/tibet-3.jpg
--------------------------------------------------------------------------------
/docs/images/tibet-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/tibet-4.jpg
--------------------------------------------------------------------------------
/docs/images/tibet-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/tibet-5.jpg
--------------------------------------------------------------------------------
/docs/images/tibet-6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/tibet-6.jpg
--------------------------------------------------------------------------------
/docs/images/tibet-7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/tibet-7.jpg
--------------------------------------------------------------------------------
/docs/images/tibet-8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/tibet-8.jpg
--------------------------------------------------------------------------------
/docs/images/tibet-9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/tibet-9.jpg
--------------------------------------------------------------------------------
/docs/images/thumbnails/tibet-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/thumbnails/tibet-1.jpg
--------------------------------------------------------------------------------
/docs/images/thumbnails/tibet-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/thumbnails/tibet-2.jpg
--------------------------------------------------------------------------------
/docs/images/thumbnails/tibet-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/thumbnails/tibet-3.jpg
--------------------------------------------------------------------------------
/docs/images/thumbnails/tibet-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/thumbnails/tibet-4.jpg
--------------------------------------------------------------------------------
/docs/images/thumbnails/tibet-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/thumbnails/tibet-5.jpg
--------------------------------------------------------------------------------
/docs/images/thumbnails/tibet-6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/thumbnails/tibet-6.jpg
--------------------------------------------------------------------------------
/docs/images/thumbnails/tibet-7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/thumbnails/tibet-7.jpg
--------------------------------------------------------------------------------
/docs/images/thumbnails/tibet-8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/thumbnails/tibet-8.jpg
--------------------------------------------------------------------------------
/docs/images/thumbnails/tibet-9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fengyuanchen/viewer/HEAD/docs/images/thumbnails/tibet-9.jpg
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js: node
3 | cache: npm
4 | script:
5 | - npm run lint
6 | - npm run build
7 | - npm test
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = lf
6 | indent_size = 2
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.stylelintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "stylelint-config-standard",
3 | "plugins": [
4 | "stylelint-order"
5 | ],
6 | "rules": {
7 | "no-descending-specificity": null,
8 | "order/properties-alphabetical-order": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | modules: false,
7 | },
8 | ],
9 | ],
10 | env: {
11 | test: {
12 | plugins: [
13 | 'istanbul',
14 | ],
15 | },
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | const rollupConfig = require('./rollup.config');
2 |
3 | module.exports = {
4 | plugins: {
5 | 'postcss-import': {},
6 | 'postcss-preset-env': {
7 | stage: 3,
8 | features: {
9 | 'nesting-rules': true,
10 | },
11 | },
12 | 'postcss-url': {
13 | url: 'inline',
14 | },
15 | 'postcss-header': {
16 | header: rollupConfig.output[0].banner,
17 | },
18 | stylelint: {
19 | fix: true,
20 | },
21 | },
22 | };
23 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb-base",
3 | "env": {
4 | "browser": true
5 | },
6 | "root": true,
7 | "rules": {
8 | "import/no-extraneous-dependencies": "off",
9 | "no-param-reassign": "off",
10 | "no-restricted-properties": "off",
11 | "valid-jsdoc": ["error", {
12 | "requireReturn": false
13 | }]
14 | },
15 | "overrides": [
16 | {
17 | "files": "test/**/*.js",
18 | "env": {
19 | "jquery": true,
20 | "mocha": true
21 | },
22 | "globals": {
23 | "expect": true
24 | },
25 | "rules": {
26 | "no-unused-expressions": "off"
27 | }
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright 2015-present Chen Fengyuan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | const babel = require('rollup-plugin-babel');
2 | const commonjs = require('rollup-plugin-commonjs');
3 | const nodeResolve = require('rollup-plugin-node-resolve');
4 | const puppeteer = require('puppeteer');
5 |
6 | process.env.CHROME_BIN = puppeteer.executablePath();
7 | process.env.NODE_ENV = 'test';
8 |
9 | module.exports = (config) => {
10 | config.set({
11 | autoWatch: false,
12 | browsers: ['ChromeHeadless'],
13 | client: {
14 | mocha: {
15 | timeout: 10000,
16 | },
17 | },
18 | coverageIstanbulReporter: {
19 | reports: ['html', 'lcovonly', 'text-summary'],
20 | },
21 | files: [
22 | 'dist/viewer.css',
23 | 'test/index.js',
24 | {
25 | pattern: 'docs/images/*',
26 | included: false,
27 | },
28 | ],
29 | frameworks: ['mocha', 'chai'],
30 | preprocessors: {
31 | 'test/index.js': ['rollup'],
32 | },
33 | reporters: ['mocha', 'coverage-istanbul'],
34 | rollupPreprocessor: {
35 | output: {
36 | format: 'iife',
37 | name: 'Anonymous',
38 | sourcemap: 'inline',
39 | },
40 | plugins: [
41 | nodeResolve(),
42 | commonjs(),
43 | babel(),
44 | ],
45 | },
46 | singleRun: true,
47 | });
48 | };
49 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | const babel = require('rollup-plugin-babel');
2 | const commonjs = require('rollup-plugin-commonjs');
3 | const nodeResolve = require('rollup-plugin-node-resolve');
4 | const changeCase = require('change-case');
5 | const createBanner = require('create-banner');
6 | const pkg = require('./package');
7 |
8 | pkg.name = pkg.name.replace('image', '');
9 |
10 | const name = changeCase.pascalCase(pkg.name);
11 | const banner = createBanner({
12 | data: {
13 | name,
14 | year: '2015-present',
15 | },
16 | });
17 |
18 | module.exports = {
19 | input: 'src/index.js',
20 | output: [
21 | {
22 | banner,
23 | name,
24 | file: `dist/${pkg.name}.js`,
25 | format: 'umd',
26 | globals: {
27 | jquery: 'jQuery',
28 | },
29 | },
30 | {
31 | banner,
32 | file: `dist/${pkg.name}.common.js`,
33 | format: 'cjs',
34 | },
35 | {
36 | banner,
37 | file: `dist/${pkg.name}.esm.js`,
38 | format: 'es',
39 | },
40 | {
41 | banner,
42 | name,
43 | file: `docs/js/${pkg.name}.js`,
44 | format: 'umd',
45 | globals: {
46 | jquery: 'jQuery',
47 | },
48 | },
49 | ],
50 | external: ['jquery'],
51 | plugins: [
52 | nodeResolve(),
53 | commonjs(),
54 | babel(),
55 | ],
56 | };
57 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import $ from 'jquery';
2 | import Viewer from 'viewerjs/src';
3 |
4 | if ($.fn) {
5 | const AnotherViewer = $.fn.viewer;
6 | const NAMESPACE = 'viewer';
7 |
8 | $.fn.viewer = function jQueryViewer(option, ...args) {
9 | let result;
10 |
11 | this.each((i, element) => {
12 | const $element = $(element);
13 | const isDestroy = option === 'destroy';
14 | let viewer = $element.data(NAMESPACE);
15 |
16 | if (!viewer) {
17 | if (isDestroy) {
18 | return;
19 | }
20 |
21 | const options = $.extend({}, $element.data(), $.isPlainObject(option) && option);
22 |
23 | viewer = new Viewer(element, options);
24 | $element.data(NAMESPACE, viewer);
25 | }
26 |
27 | if (typeof option === 'string') {
28 | const fn = viewer[option];
29 |
30 | if ($.isFunction(fn)) {
31 | result = fn.apply(viewer, args);
32 |
33 | if (result === viewer) {
34 | result = undefined;
35 | }
36 |
37 | if (isDestroy) {
38 | $element.removeData(NAMESPACE);
39 | }
40 | }
41 | }
42 | });
43 |
44 | return result !== undefined ? result : this;
45 | };
46 |
47 | $.fn.viewer.Constructor = Viewer;
48 | $.fn.viewer.setDefaults = Viewer.setDefaults;
49 | $.fn.viewer.noConflict = function noConflict() {
50 | $.fn.viewer = AnotherViewer;
51 | return this;
52 | };
53 | }
54 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | import $ from 'jquery';
2 | import '../src';
3 |
4 | describe('viewer', () => {
5 | const createImage = () => {
6 | const container = document.createElement('div');
7 | const image = document.createElement('img');
8 |
9 | image.src = '/base/docs/images/tibet-1.jpg';
10 | container.appendChild(image);
11 | document.body.appendChild(container);
12 |
13 | return image;
14 | };
15 |
16 | it('should register as a plugin correctly', () => {
17 | expect($.fn.viewer).to.be.a('function');
18 | expect($.fn.viewer.Constructor).to.be.a('function');
19 | expect($.fn.viewer.noConflict).to.be.a('function');
20 | expect($.fn.viewer.setDefaults).to.be.a('function');
21 | });
22 |
23 | it('should remove data after destroyed', () => {
24 | const $image = $(createImage());
25 |
26 | $image.viewer();
27 | expect($image.data('viewer')).to.be.an.instanceof($.fn.viewer.Constructor);
28 | $image.viewer('destroy');
29 | expect($image.data('viewer')).to.be.undefined;
30 | });
31 |
32 | it('should apply the given option', (done) => {
33 | $(createImage()).viewer({
34 | inline: true,
35 |
36 | ready() {
37 | done();
38 | },
39 | });
40 | });
41 |
42 | it('should execute the given method', (done) => {
43 | $(createImage()).viewer({
44 | shown() {
45 | done();
46 | },
47 | }).viewer('show');
48 | });
49 |
50 | it('should trigger the binding event', (done) => {
51 | $(createImage()).one('ready', (event) => {
52 | expect(event.type).to.equal('ready');
53 | done();
54 | }).viewer('show');
55 | });
56 |
57 | it('should rollback when call the $.fn.viewer.conflict', () => {
58 | const { viewer } = $.fn;
59 | const noConflictViewer = $.fn.viewer.noConflict();
60 |
61 | expect(noConflictViewer).to.equal(viewer);
62 | expect($.fn.viewer).to.be.undefined;
63 |
64 | // Reverts it for the rest test suites
65 | $.fn.viewer = noConflictViewer;
66 | });
67 | });
68 |
--------------------------------------------------------------------------------
/docs/css/main.css:
--------------------------------------------------------------------------------
1 | .btn {
2 | padding-left: 0.75rem;
3 | padding-right: 0.75rem;
4 | }
5 |
6 | .list-group-item {
7 | padding: 0.0625rem 1rem;
8 | }
9 |
10 | .d-flex > .btn {
11 | flex: 1;
12 | }
13 |
14 | .carbonads {
15 | border: 1px solid #ccc;
16 | border-radius: 0.25rem;
17 | font-size: 0.875rem;
18 | overflow: hidden;
19 | padding: 1rem;
20 | }
21 |
22 | .carbon-wrap {
23 | overflow: hidden;
24 | }
25 |
26 | .carbon-img {
27 | clear: left;
28 | display: block;
29 | float: left;
30 | }
31 |
32 | .carbon-text,
33 | .carbon-poweredby {
34 | display: block;
35 | margin-left: 140px;
36 | }
37 |
38 | .carbon-text,
39 | .carbon-text:hover,
40 | .carbon-text:focus {
41 | color: #fff;
42 | text-decoration: none;
43 | }
44 |
45 | .carbon-poweredby,
46 | .carbon-poweredby:hover,
47 | .carbon-poweredby:focus {
48 | color: #ddd;
49 | text-decoration: none;
50 | }
51 |
52 | @media (min-width: 768px) {
53 | .carbonads {
54 | float: right;
55 | margin-bottom: -1rem;
56 | margin-top: -1rem;
57 | max-width: 360px;
58 | }
59 | }
60 |
61 | .footer {
62 | font-size: 0.875rem;
63 | }
64 |
65 | .heart {
66 | color: #ddd;
67 | display: block;
68 | height: 2rem;
69 | line-height: 2rem;
70 | margin-bottom: 0;
71 | margin-top: 1rem;
72 | position: relative;
73 | text-align: center;
74 | width: 100%;
75 | }
76 |
77 | .heart:hover {
78 | color: #ff4136;
79 | }
80 |
81 | .heart::before {
82 | border-top: 1px solid #eee;
83 | content: " ";
84 | display: block;
85 | height: 0;
86 | left: 0;
87 | position: absolute;
88 | right: 0;
89 | top: 50%;
90 | }
91 |
92 | .heart::after {
93 | background-color: #fff;
94 | content: "♥";
95 | padding-left: 0.5rem;
96 | padding-right: 0.5rem;
97 | position: relative;
98 | z-index: 1;
99 | }
100 |
101 | .docs-pictures {
102 | list-style: none;
103 | margin: 0;
104 | padding: 0;
105 | }
106 |
107 | .docs-pictures > li {
108 | border: 1px solid transparent;
109 | float: left;
110 | height: calc(100% / 3);
111 | margin: 0 -1px -1px 0;
112 | overflow: hidden;
113 | width: calc(100% / 3);
114 | }
115 |
116 | .docs-pictures > li > img {
117 | cursor: -webkit-zoom-in;
118 | cursor: zoom-in;
119 | width: 100%;
120 | }
121 |
122 | .docs-buttons > .btn-group,
123 | .docs-buttons > .input-group {
124 | margin-bottom: 5px;
125 | width: 100%;
126 | }
127 |
128 | .docs-buttons .input-group-prepend {
129 | width: 50%;
130 | }
131 |
132 | .docs-buttons .input-group-prepend .btn {
133 | width: 100%;
134 | }
135 |
--------------------------------------------------------------------------------
/docs/js/main.js:
--------------------------------------------------------------------------------
1 | $(function () {
2 | 'use strict';
3 |
4 | var console = window.console || { log: function () {} };
5 | var $images = $('.docs-pictures');
6 | var $toggles = $('.docs-toggles');
7 | var $buttons = $('.docs-buttons');
8 | var options = {
9 | // inline: true,
10 | url: 'data-original',
11 | ready: function (e) {
12 | console.log(e.type);
13 | },
14 | show: function (e) {
15 | console.log(e.type);
16 | },
17 | shown: function (e) {
18 | console.log(e.type);
19 | },
20 | hide: function (e) {
21 | console.log(e.type);
22 | },
23 | hidden: function (e) {
24 | console.log(e.type);
25 | },
26 | view: function (e) {
27 | console.log(e.type);
28 | },
29 | viewed: function (e) {
30 | console.log(e.type);
31 | }
32 | };
33 |
34 | function toggleButtons(mode) {
35 | if (/modal|inline|none/.test(mode)) {
36 | $buttons
37 | .find('button[data-enable]')
38 | .prop('disabled', true)
39 | .filter('[data-enable*="' + mode + '"]')
40 | .prop('disabled', false);
41 | }
42 | }
43 |
44 | $images.on({
45 | ready: function (e) {
46 | console.log(e.type);
47 | },
48 | show: function (e) {
49 | console.log(e.type);
50 | },
51 | shown: function (e) {
52 | console.log(e.type);
53 | },
54 | hide: function (e) {
55 | console.log(e.type);
56 | },
57 | hidden: function (e) {
58 | console.log(e.type);
59 | },
60 | view: function (e) {
61 | console.log(e.type);
62 | },
63 | viewed: function (e) {
64 | console.log(e.type);
65 | }
66 | }).viewer(options);
67 |
68 | toggleButtons(options.inline ? 'inline' : 'modal');
69 |
70 | $toggles.on('change', 'input', function () {
71 | var $input = $(this);
72 | var name = $input.attr('name');
73 |
74 | options[name] = name === 'inline' ? $input.data('value') : $input.prop('checked');
75 | $images.viewer('destroy').viewer(options);
76 | toggleButtons(options.inline ? 'inline' : 'modal');
77 | });
78 |
79 | $buttons.on('click', 'button', function () {
80 | var data = $(this).data();
81 | var args = data.arguments || [];
82 |
83 | if (data.method) {
84 | if (data.target) {
85 | $images.viewer(data.method, $(data.target).val());
86 | } else {
87 | $images.viewer(data.method, args[0], args[1]);
88 | }
89 |
90 | switch (data.method) {
91 | case 'scaleX':
92 | case 'scaleY':
93 | args[0] = -args[0];
94 | break;
95 |
96 | case 'destroy':
97 | toggleButtons('none');
98 | break;
99 | }
100 | }
101 | });
102 |
103 | $('[data-toggle="tooltip"]').tooltip();
104 | });
105 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 1.1.0 (Dec 14 2019)
4 |
5 | - Upgrade the built-in Viewer.js to v1.5.0.
6 |
7 | ## 1.0.0 (Apr 1, 2018)
8 |
9 | - Upgrade Viewer.js to v1.0.0.
10 |
11 | ## 1.0.0-beta (Mar 15, 2018)
12 |
13 | - Upgrade Viewer.js to v1.0.0-rc.1.
14 |
15 | ## 1.0.0-alpha (Mar 11, 2018)
16 |
17 | - The core code of Viewer is replaced with [Viewer.js](https://github.com/fengyuanchen/viewerjs) now.
18 |
19 | ## 0.7.0 (Mar 11, 2018)
20 |
21 | - Emulate scroll bar width when modal opening.
22 | - Disallow to show again if it had shown.
23 |
24 | ## 0.6.0 (Oct 7, 2017)
25 |
26 | - Refactor in ES6.
27 | - Build CSS code with PostCSS.
28 | - Removed `build` event.
29 | - Renamed `built` event to `ready`.
30 | - Removed event namespace.
31 |
32 | ## 0.5.1 (Mar 11, 2016)
33 |
34 | - Fixed the issue of the "button" option (#8).
35 | - Fixed the issue of the "$.fn.viewer.setDefault" static method (#9).
36 |
37 | ## 0.5.0 (Jan 21, 2016)
38 |
39 | - Add more available values to the "title", "toolbar" and "navbar" options.
40 | - Support to toggle the visibility of title, toolbar and navbar between different screen widths.
41 | - Exit fullscreen when stop playing.
42 | - Fixed title not generated bug (#6).
43 |
44 | ## 0.4.0 (Jan 1, 2016)
45 |
46 | - Added "update" method for update image dynamically.
47 | - Hides title and toolbar on small screen (width < 768px).
48 |
49 | ## 0.3.1 (Dec 28, 2015)
50 |
51 | - Supports to zoom from event triggering point.
52 | - Fix a bug of the index of viewing image.
53 |
54 | ## 0.3.0 (Dec 24, 2015)
55 |
56 | - Add 2 new options: "view" and "viewed"
57 | - Add 2 new events: "view" and "viewed"
58 | - Add keyboard support: stop playing when tap the `Space` key
59 | - Fix lost transition after call full method in inline mode
60 | - Fix incorrect tooltip after switch image quickly
61 |
62 | ## 0.2.0 (Oct 18, 2015)
63 |
64 | - Added one new method: "moveTo"
65 | - Improved the image loading and showing
66 |
67 | ## 0.1.1 (Oct 7, 2015)
68 |
69 | - Fixed the issue of modal closing after zoomed in and zoomed out
70 |
71 | ## 0.1.0 (Sep 2, 2015)
72 |
73 | - Supports 2 modes: "modal" (default), "inline"
74 | - Supports 28 options: "inline", "button", "navbar", "title", "toolbar", "tooltip", "movable", "zoomable", "rotatable", "scalable", "transition", "fullscreen", "keyboard", "interval", "minWidth", "minHeight", "zoomRatio", "minZoomRatio", "maxZoomRatio", "zIndex", "zIndexInline", "url", "build", "built", "show", "shown", "hide", "hidden"
75 | - Supports 21 methods: "show", "hide", "view", "prev", "next", "move", "zoom", "zoomTo", "rotate", "rotateTo", "scale", "scaleX", "scaleY", "play", "stop", "full", "exit", "tooltip", "toggle", "reset", "destroy"
76 | - Supports 6 events: "build.viewer", "built.viewer", "show.viewer", "shown.viewer", "hide.viewer", "hidden.viewer"
77 |
78 | ## 0.0.1 (Apr 19, 2015)
79 |
80 | - Improve UI
81 | - Develop a alpha version
82 |
83 | ## 0.0.0 (Apr 19, 2014)
84 |
85 | - Design UI
86 | - Develop a draft version
87 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Viewer
2 |
3 | [](https://travis-ci.org/fengyuanchen/viewer) [](https://www.npmjs.com/package/imageviewer) [](https://www.npmjs.com/package/imageviewer) [](https://www.npmjs.com/package/imageviewer)
4 |
5 | > A simple jQuery image viewing plugin. As of v1.0.0, the core code of Viewer is replaced with [Viewer.js](https://github.com/fengyuanchen/viewerjs).
6 |
7 | - [Demo](https://fengyuanchen.github.io/viewer)
8 | - [Viewer.js](https://github.com/fengyuanchen/viewerjs) - JavaScript image viewer (**recommended**)
9 | - [jquery-viewer](https://github.com/fengyuanchen/jquery-viewer) - A jQuery plugin wrapper for Viewer.js (**recommended** for jQuery users to use this instead of Viewer)
10 |
11 | ## Main
12 |
13 | ```text
14 | dist/
15 | ├── viewer.css
16 | ├── viewer.min.css (compressed)
17 | ├── viewer.js (UMD)
18 | ├── viewer.min.js (UMD, compressed)
19 | ├── viewer.common.js (CommonJS, default)
20 | └── viewer.esm.js (ES Module)
21 | ```
22 |
23 | ## Getting started
24 |
25 | ### Installation
26 |
27 | ```shell
28 | npm install imageviewer jquery
29 | ```
30 |
31 | Include files:
32 |
33 | ```html
34 |
35 |
36 |
37 | ```
38 |
39 | ### Usage
40 |
41 | Initialize with `$.fn.viewer` method.
42 |
43 | ```html
44 |
45 |
46 |

47 |
48 |
49 |
56 | ```
57 |
58 | ```js
59 | var $image = $('#image');
60 |
61 | $image.viewer({
62 | inline: true,
63 | viewed: function() {
64 | $image.viewer('zoomTo', 1);
65 | }
66 | });
67 |
68 | // Get the Viewer.js instance after initialized
69 | var viewer = $image.data('viewer');
70 |
71 | // View a list of images
72 | $('#images').viewer();
73 | ```
74 |
75 | ## Options
76 |
77 | See the available [options](https://github.com/fengyuanchen/viewerjs#options) of Viewer.js.
78 |
79 | ```js
80 | $().viewer(options);
81 | ```
82 |
83 | ## Methods
84 |
85 | See the available [methods](https://github.com/fengyuanchen/viewerjs#methods) of Viewer.js.
86 |
87 | ```js
88 | $().viewer('method', argument1, , argument2, ..., argumentN);
89 | ```
90 |
91 | ## Events
92 |
93 | See the available [events](https://github.com/fengyuanchen/viewerjs#events) of Viewer.js.
94 |
95 | ```js
96 | $().on('event', handler);
97 | ```
98 |
99 | ## No conflict
100 |
101 | If you have to use other plugin with the same namespace, just call the `$.fn.viewer.noConflict` method to revert to it.
102 |
103 | ```html
104 |
105 |
106 |
110 | ```
111 |
112 | ## Browser support
113 |
114 | It is the same as the [browser support of Viewer.js](https://github.com/fengyuanchen/viewerjs#browser-support). As a jQuery plugin, you also need to see the [jQuery Browser Support](https://jquery.com/browser-support/).
115 |
116 | ## Versioning
117 |
118 | Maintained under the [Semantic Versioning guidelines](https://semver.org/).
119 |
120 | ## License
121 |
122 | [MIT](https://opensource.org/licenses/MIT) © [Chen Fengyuan](https://chenfengyuan.com/)
123 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "imageviewer",
3 | "description": "A simple jQuery image viewing plugin.",
4 | "version": "1.1.0",
5 | "main": "dist/viewer.common.js",
6 | "module": "dist/viewer.esm.js",
7 | "browser": "dist/viewer.js",
8 | "style": "dist/viewer.css",
9 | "files": [
10 | "src",
11 | "dist"
12 | ],
13 | "scripts": {
14 | "build": "npm run build:css && npm run build:js",
15 | "build:css": "postcss src/index.css -o dist/viewer.css --no-map",
16 | "build:js": "rollup -c",
17 | "clear": "del-cli dist",
18 | "compress": "npm run compress:css && npm run compress:js",
19 | "compress:css": "postcss dist/viewer.css -u cssnano -o dist/viewer.min.css --no-map",
20 | "compress:js": "uglifyjs dist/viewer.js -o dist/viewer.min.js -c -m --comments /^!/",
21 | "copy": "cpy dist/viewer.css docs/css",
22 | "lint": "eslint src test *.js --fix",
23 | "release": "npm run clear && npm run lint && npm run build && npm run compress && npm run copy && npm test",
24 | "start": "npm-run-all --parallel watch:*",
25 | "test": "karma start",
26 | "watch:css": "postcss src/index.css -o docs/css/viewer.css -m -w",
27 | "watch:js": "rollup -c -m -w"
28 | },
29 | "repository": {
30 | "type": "git",
31 | "url": "git+https://github.com/fengyuanchen/viewer.git"
32 | },
33 | "keywords": [
34 | "image",
35 | "viewer",
36 | "jquery",
37 | "jquery-plugin"
38 | ],
39 | "author": {
40 | "name": "Chen Fengyuan",
41 | "url": "https://chenfengyuan.com/"
42 | },
43 | "license": "MIT",
44 | "bugs": {
45 | "url": "https://github.com/fengyuanchen/viewer/issues"
46 | },
47 | "homepage": "https://fengyuanchen.github.io/viewer",
48 | "dependencies": {
49 | "viewerjs": "^1.5.0"
50 | },
51 | "devDependencies": {
52 | "@babel/core": "^7.7.5",
53 | "@babel/preset-env": "^7.7.6",
54 | "@commitlint/cli": "^8.2.0",
55 | "@commitlint/config-conventional": "^8.2.0",
56 | "babel-plugin-istanbul": "^5.2.0",
57 | "chai": "^4.2.0",
58 | "change-case": "^4.1.0",
59 | "cpy-cli": "^3.0.0",
60 | "create-banner": "^1.0.0",
61 | "cssnano": "^4.1.10",
62 | "del-cli": "^3.0.0",
63 | "eslint": "^6.7.2",
64 | "eslint-config-airbnb-base": "^14.0.0",
65 | "eslint-plugin-import": "^2.19.1",
66 | "husky": "^3.1.0",
67 | "jquery": "^3.4.1",
68 | "karma": "^4.4.1",
69 | "karma-chai": "^0.1.0",
70 | "karma-chrome-launcher": "^3.1.0",
71 | "karma-coverage-istanbul-reporter": "^2.1.1",
72 | "karma-mocha": "^1.3.0",
73 | "karma-mocha-reporter": "^2.2.5",
74 | "karma-rollup-preprocessor": "^7.0.2",
75 | "lint-staged": "^9.5.0",
76 | "mocha": "^6.2.2",
77 | "npm-run-all": "^4.1.5",
78 | "postcss-cli": "^6.1.3",
79 | "postcss-header": "^1.0.0",
80 | "postcss-import": "^12.0.1",
81 | "postcss-preset-env": "^6.7.0",
82 | "postcss-url": "^8.0.0",
83 | "puppeteer": "^2.0.0",
84 | "rollup": "^1.27.12",
85 | "rollup-plugin-babel": "^4.3.3",
86 | "rollup-plugin-commonjs": "^10.1.0",
87 | "rollup-plugin-node-resolve": "^5.2.0",
88 | "stylelint": "^12.0.0",
89 | "stylelint-config-standard": "^19.0.0",
90 | "stylelint-order": "^3.1.1",
91 | "uglify-js": "^3.7.2"
92 | },
93 | "peerDependencies": {
94 | "jquery": ">=1.9.1"
95 | },
96 | "browserslist": [
97 | "last 2 versions",
98 | "> 1%",
99 | "not ie <= 8"
100 | ],
101 | "commitlint": {
102 | "extends": [
103 | "@commitlint/config-conventional"
104 | ]
105 | },
106 | "husky": {
107 | "hooks": {
108 | "pre-commit": "lint-staged",
109 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
110 | }
111 | },
112 | "lint-staged": {
113 | "{src,test}/**/*.js|*.conf*.js": [
114 | "eslint --fix",
115 | "git add"
116 | ],
117 | "{src,docs}/**/*.{css,html}": [
118 | "stylelint --fix",
119 | "git add"
120 | ]
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/dist/viewer.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Viewer v1.1.0
3 | * https://fengyuanchen.github.io/viewer
4 | *
5 | * Copyright 2015-present Chen Fengyuan
6 | * Released under the MIT license
7 | *
8 | * Date: 2019-12-14T11:48:37.240Z
9 | */.viewer-close:before,.viewer-flip-horizontal:before,.viewer-flip-vertical:before,.viewer-fullscreen-exit:before,.viewer-fullscreen:before,.viewer-next:before,.viewer-one-to-one:before,.viewer-play:before,.viewer-prev:before,.viewer-reset:before,.viewer-rotate-left:before,.viewer-rotate-right:before,.viewer-zoom-in:before,.viewer-zoom-out:before{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAAUCAYAAABWOyJDAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAQPSURBVHic7Zs/iFxVFMa/0U2UaJGksUgnIVhYxVhpjDbZCBmLdAYECxsRFBTUamcXUiSNncgKQbSxsxH8gzAP3FU2jY0kKKJNiiiIghFlccnP4p3nPCdv3p9778vsLOcHB2bfveeb7955c3jvvNkBIMdxnD64a94GHMfZu3iBcRynN7zAOI7TG15gHCeeNUkr8zaxG2lbYDYsdgMbktBsP03jdQwljSXdtBhLOmtjowC9Mg9L+knSlcD8TNKpSA9lBpK2JF2VdDSR5n5J64m0qli399hNFMUlpshQii5jbXTbHGviB0nLNeNDSd9VO4A2UdB2fp+x0eCnaXxWXGA2X0au/3HgN9P4LFCjIANOJdrLr0zzZ+BEpNYDwKbpnQMeAw4m8HjQtM6Z9qa917zPQwFr3M5KgA6J5rTJCdFZJj9/lyvGhsDvwFNVuV2MhhjrK6b9bFiE+j1r87eBl4HDwCF7/U/k+ofAX5b/EXBv5JoLMuILzf3Ap6Z3EzgdqHMCuF7hcQf4HDgeoHnccncqdK/TvSDWffFXI/exICY/xZyqc6XLWF1UFZna4gJ7q8BsRvgd2/xXpo6P+D9dfT7PpECtA3cnWPM0GXGFZh/wgWltA+cDNC7X+AP4GzjZQe+k5dRxuYPeiuXU7e1qwLpDz7dFjXKRaSwuMLvAlG8zZlG+YmiK1HoFqT7wP2z+4Q45TfEGcMt01xLoNZEBTwRqD4BLpnMLeC1A41UmVxsXgXeBayV/Wx20rpTyrpnWRft7p6O/FdqzGrDukPNtkaMoMo3FBdBSQMOnYBCReyf05s126fU9ytfX98+mY54Kxnp7S9K3kj6U9KYdG0h6UdLbkh7poFXMfUnSOyVvL0h6VtIXHbS6nOP+s/Zm9mvyXW1uuC9ohZ72E9uDmXWLJOB1GxsH+DxPftsB8B6wlGDN02TAkxG6+4D3TWsbeC5CS8CDFce+AW500LhhOW2020TRjK3b21HEmgti9m0RonxbdMZeVzV+/4tF3cBpP7E9mKHNL5q8h5g0eYsCMQz0epq8gQrwMXAgcs0FGXGFRcB9wCemF9PkbYqM/Bas7fxLwNeJPdTdpo4itQti8lPMqTpXuozVRVXPpbHI3KkNTB1NfkL81j2mvhDp91HgV9MKuRIqrykj3WPq4rHyL+axj8/qGPmTqi6F9YDlHOvJU6oYcTsh/TYSzWmTE6JT19CtLTJt32D6CmHe0eQn1O8z5AXgT4sx4Vcu0/EQecMydB8z0hUWkTd2t4CrwNEePqMBcAR4mrBbwyXLPWJa8zrXmmLEhNBmfpkuY2102xxrih+pb+ieAb6vGhuA97UcJ5KR8gZ77K+99xxeYBzH6Q3/Z0fHcXrDC4zjOL3hBcZxnN74F+zlvXFWXF9PAAAAAElFTkSuQmCC");background-repeat:no-repeat;background-size:280px;color:transparent;display:block;font-size:0;height:20px;line-height:0;width:20px}.viewer-zoom-in:before{background-position:0 0;content:"Zoom In"}.viewer-zoom-out:before{background-position:-20px 0;content:"Zoom Out"}.viewer-one-to-one:before{background-position:-40px 0;content:"One to One"}.viewer-reset:before{background-position:-60px 0;content:"Reset"}.viewer-prev:before{background-position:-80px 0;content:"Previous"}.viewer-play:before{background-position:-100px 0;content:"Play"}.viewer-next:before{background-position:-120px 0;content:"Next"}.viewer-rotate-left:before{background-position:-140px 0;content:"Rotate Left"}.viewer-rotate-right:before{background-position:-160px 0;content:"Rotate Right"}.viewer-flip-horizontal:before{background-position:-180px 0;content:"Flip Horizontal"}.viewer-flip-vertical:before{background-position:-200px 0;content:"Flip Vertical"}.viewer-fullscreen:before{background-position:-220px 0;content:"Enter Full Screen"}.viewer-fullscreen-exit:before{background-position:-240px 0;content:"Exit Full Screen"}.viewer-close:before{background-position:-260px 0;content:"Close"}.viewer-container{bottom:0;direction:ltr;font-size:0;left:0;line-height:0;overflow:hidden;position:absolute;right:0;-webkit-tap-highlight-color:transparent;top:0;-ms-touch-action:none;touch-action:none;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.viewer-container::-moz-selection,.viewer-container ::-moz-selection{background-color:transparent}.viewer-container::selection,.viewer-container ::selection{background-color:transparent}.viewer-container img{display:block;height:auto;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.viewer-canvas{bottom:0;left:0;overflow:hidden;position:absolute;right:0;top:0}.viewer-canvas>img{height:auto;margin:15px auto;max-width:90%!important;width:auto}.viewer-footer{bottom:0;left:0;overflow:hidden;position:absolute;right:0;text-align:center}.viewer-navbar{background-color:rgba(0,0,0,.5);overflow:hidden}.viewer-list{-webkit-box-sizing:content-box;box-sizing:content-box;height:50px;margin:0;overflow:hidden;padding:1px 0}.viewer-list>li{color:transparent;cursor:pointer;float:left;font-size:0;height:50px;line-height:0;opacity:.5;overflow:hidden;-webkit-transition:opacity .15s;transition:opacity .15s;width:30px}.viewer-list>li:hover{opacity:.75}.viewer-list>li+li{margin-left:1px}.viewer-list>.viewer-loading{position:relative}.viewer-list>.viewer-loading:after{border-width:2px;height:20px;margin-left:-10px;margin-top:-10px;width:20px}.viewer-list>.viewer-active,.viewer-list>.viewer-active:hover{opacity:1}.viewer-player{background-color:#000;bottom:0;cursor:none;display:none;right:0}.viewer-player,.viewer-player>img{left:0;position:absolute;top:0}.viewer-toolbar>ul{display:inline-block;margin:0 auto 5px;overflow:hidden;padding:3px 0}.viewer-toolbar>ul>li{background-color:rgba(0,0,0,.5);border-radius:50%;cursor:pointer;float:left;height:24px;overflow:hidden;-webkit-transition:background-color .15s;transition:background-color .15s;width:24px}.viewer-toolbar>ul>li:hover{background-color:rgba(0,0,0,.8)}.viewer-toolbar>ul>li:before{margin:2px}.viewer-toolbar>ul>li+li{margin-left:1px}.viewer-toolbar>ul>.viewer-small{height:18px;margin-bottom:3px;margin-top:3px;width:18px}.viewer-toolbar>ul>.viewer-small:before{margin:-1px}.viewer-toolbar>ul>.viewer-large{height:30px;margin-bottom:-3px;margin-top:-3px;width:30px}.viewer-toolbar>ul>.viewer-large:before{margin:5px}.viewer-tooltip{background-color:rgba(0,0,0,.8);border-radius:10px;color:#fff;display:none;font-size:12px;height:20px;left:50%;line-height:20px;margin-left:-25px;margin-top:-10px;position:absolute;text-align:center;top:50%;width:50px}.viewer-title{color:#ccc;display:inline-block;font-size:12px;line-height:1;margin:0 5% 5px;max-width:90%;opacity:.8;overflow:hidden;text-overflow:ellipsis;-webkit-transition:opacity .15s;transition:opacity .15s;white-space:nowrap}.viewer-title:hover{opacity:1}.viewer-button{background-color:rgba(0,0,0,.5);border-radius:50%;cursor:pointer;height:80px;overflow:hidden;position:absolute;right:-40px;top:-40px;-webkit-transition:background-color .15s;transition:background-color .15s;width:80px}.viewer-button:focus,.viewer-button:hover{background-color:rgba(0,0,0,.8)}.viewer-button:before{bottom:15px;left:15px;position:absolute}.viewer-fixed{position:fixed}.viewer-open{overflow:hidden}.viewer-show{display:block}.viewer-hide{display:none}.viewer-backdrop{background-color:rgba(0,0,0,.5)}.viewer-invisible{visibility:hidden}.viewer-move{cursor:move;cursor:-webkit-grab;cursor:grab}.viewer-fade{opacity:0}.viewer-in{opacity:1}.viewer-transition{-webkit-transition:all .3s;transition:all .3s}@-webkit-keyframes viewer-spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes viewer-spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}.viewer-loading:after{-webkit-animation:viewer-spinner 1s linear infinite;animation:viewer-spinner 1s linear infinite;border:4px solid hsla(0,0%,100%,.1);border-left-color:hsla(0,0%,100%,.5);border-radius:50%;content:"";display:inline-block;height:40px;left:50%;margin-left:-20px;margin-top:-20px;position:absolute;top:50%;width:40px;z-index:1}@media (max-width:767px){.viewer-hide-xs-down{display:none}}@media (max-width:991px){.viewer-hide-sm-down{display:none}}@media (max-width:1199px){.viewer-hide-md-down{display:none}}
--------------------------------------------------------------------------------
/dist/viewer.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Viewer v1.1.0
3 | * https://fengyuanchen.github.io/viewer
4 | *
5 | * Copyright 2015-present Chen Fengyuan
6 | * Released under the MIT license
7 | *
8 | * Date: 2019-12-14T11:48:37.240Z
9 | */
10 |
11 | .viewer-zoom-in::before,
12 | .viewer-zoom-out::before,
13 | .viewer-one-to-one::before,
14 | .viewer-reset::before,
15 | .viewer-prev::before,
16 | .viewer-play::before,
17 | .viewer-next::before,
18 | .viewer-rotate-left::before,
19 | .viewer-rotate-right::before,
20 | .viewer-flip-horizontal::before,
21 | .viewer-flip-vertical::before,
22 | .viewer-fullscreen::before,
23 | .viewer-fullscreen-exit::before,
24 | .viewer-close::before {
25 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAAUCAYAAABWOyJDAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAQPSURBVHic7Zs/iFxVFMa/0U2UaJGksUgnIVhYxVhpjDbZCBmLdAYECxsRFBTUamcXUiSNncgKQbSxsxH8gzAP3FU2jY0kKKJNiiiIghFlccnP4p3nPCdv3p9778vsLOcHB2bfveeb7955c3jvvNkBIMdxnD64a94GHMfZu3iBcRynN7zAOI7TG15gHCeeNUkr8zaxG2lbYDYsdgMbktBsP03jdQwljSXdtBhLOmtjowC9Mg9L+knSlcD8TNKpSA9lBpK2JF2VdDSR5n5J64m0qli399hNFMUlpshQii5jbXTbHGviB0nLNeNDSd9VO4A2UdB2fp+x0eCnaXxWXGA2X0au/3HgN9P4LFCjIANOJdrLr0zzZ+BEpNYDwKbpnQMeAw4m8HjQtM6Z9qa917zPQwFr3M5KgA6J5rTJCdFZJj9/lyvGhsDvwFNVuV2MhhjrK6b9bFiE+j1r87eBl4HDwCF7/U/k+ofAX5b/EXBv5JoLMuILzf3Ap6Z3EzgdqHMCuF7hcQf4HDgeoHnccncqdK/TvSDWffFXI/exICY/xZyqc6XLWF1UFZna4gJ7q8BsRvgd2/xXpo6P+D9dfT7PpECtA3cnWPM0GXGFZh/wgWltA+cDNC7X+AP4GzjZQe+k5dRxuYPeiuXU7e1qwLpDz7dFjXKRaSwuMLvAlG8zZlG+YmiK1HoFqT7wP2z+4Q45TfEGcMt01xLoNZEBTwRqD4BLpnMLeC1A41UmVxsXgXeBayV/Wx20rpTyrpnWRft7p6O/FdqzGrDukPNtkaMoMo3FBdBSQMOnYBCReyf05s126fU9ytfX98+mY54Kxnp7S9K3kj6U9KYdG0h6UdLbkh7poFXMfUnSOyVvL0h6VtIXHbS6nOP+s/Zm9mvyXW1uuC9ohZ72E9uDmXWLJOB1GxsH+DxPftsB8B6wlGDN02TAkxG6+4D3TWsbeC5CS8CDFce+AW500LhhOW2020TRjK3b21HEmgti9m0RonxbdMZeVzV+/4tF3cBpP7E9mKHNL5q8h5g0eYsCMQz0epq8gQrwMXAgcs0FGXGFRcB9wCemF9PkbYqM/Bas7fxLwNeJPdTdpo4itQti8lPMqTpXuozVRVXPpbHI3KkNTB1NfkL81j2mvhDp91HgV9MKuRIqrykj3WPq4rHyL+axj8/qGPmTqi6F9YDlHOvJU6oYcTsh/TYSzWmTE6JT19CtLTJt32D6CmHe0eQn1O8z5AXgT4sx4Vcu0/EQecMydB8z0hUWkTd2t4CrwNEePqMBcAR4mrBbwyXLPWJa8zrXmmLEhNBmfpkuY2102xxrih+pb+ieAb6vGhuA97UcJ5KR8gZ77K+99xxeYBzH6Q3/Z0fHcXrDC4zjOL3hBcZxnN74F+zlvXFWXF9PAAAAAElFTkSuQmCC');
26 | background-repeat: no-repeat;
27 | background-size: 280px;
28 | color: transparent;
29 | display: block;
30 | font-size: 0;
31 | height: 20px;
32 | line-height: 0;
33 | width: 20px;
34 | }
35 |
36 | .viewer-zoom-in::before {
37 | background-position: 0 0;
38 | content: 'Zoom In';
39 | }
40 |
41 | .viewer-zoom-out::before {
42 | background-position: -20px 0;
43 | content: 'Zoom Out';
44 | }
45 |
46 | .viewer-one-to-one::before {
47 | background-position: -40px 0;
48 | content: 'One to One';
49 | }
50 |
51 | .viewer-reset::before {
52 | background-position: -60px 0;
53 | content: 'Reset';
54 | }
55 |
56 | .viewer-prev::before {
57 | background-position: -80px 0;
58 | content: 'Previous';
59 | }
60 |
61 | .viewer-play::before {
62 | background-position: -100px 0;
63 | content: 'Play';
64 | }
65 |
66 | .viewer-next::before {
67 | background-position: -120px 0;
68 | content: 'Next';
69 | }
70 |
71 | .viewer-rotate-left::before {
72 | background-position: -140px 0;
73 | content: 'Rotate Left';
74 | }
75 |
76 | .viewer-rotate-right::before {
77 | background-position: -160px 0;
78 | content: 'Rotate Right';
79 | }
80 |
81 | .viewer-flip-horizontal::before {
82 | background-position: -180px 0;
83 | content: 'Flip Horizontal';
84 | }
85 |
86 | .viewer-flip-vertical::before {
87 | background-position: -200px 0;
88 | content: 'Flip Vertical';
89 | }
90 |
91 | .viewer-fullscreen::before {
92 | background-position: -220px 0;
93 | content: 'Enter Full Screen';
94 | }
95 |
96 | .viewer-fullscreen-exit::before {
97 | background-position: -240px 0;
98 | content: 'Exit Full Screen';
99 | }
100 |
101 | .viewer-close::before {
102 | background-position: -260px 0;
103 | content: 'Close';
104 | }
105 |
106 | .viewer-container {
107 | bottom: 0;
108 | direction: ltr;
109 | font-size: 0;
110 | left: 0;
111 | line-height: 0;
112 | overflow: hidden;
113 | position: absolute;
114 | right: 0;
115 | -webkit-tap-highlight-color: transparent;
116 | top: 0;
117 | -ms-touch-action: none;
118 | touch-action: none;
119 | -webkit-touch-callout: none;
120 | -webkit-user-select: none;
121 | -moz-user-select: none;
122 | -ms-user-select: none;
123 | user-select: none;
124 | }
125 |
126 | .viewer-container::-moz-selection,
127 | .viewer-container *::-moz-selection {
128 | background-color: transparent;
129 | }
130 |
131 | .viewer-container::selection,
132 | .viewer-container *::selection {
133 | background-color: transparent;
134 | }
135 |
136 | .viewer-container img {
137 | display: block;
138 | height: auto;
139 | max-height: none !important;
140 | max-width: none !important;
141 | min-height: 0 !important;
142 | min-width: 0 !important;
143 | width: 100%;
144 | }
145 |
146 | .viewer-canvas {
147 | bottom: 0;
148 | left: 0;
149 | overflow: hidden;
150 | position: absolute;
151 | right: 0;
152 | top: 0;
153 | }
154 |
155 | .viewer-canvas > img {
156 | height: auto;
157 | margin: 15px auto;
158 | max-width: 90% !important;
159 | width: auto;
160 | }
161 |
162 | .viewer-footer {
163 | bottom: 0;
164 | left: 0;
165 | overflow: hidden;
166 | position: absolute;
167 | right: 0;
168 | text-align: center;
169 | }
170 |
171 | .viewer-navbar {
172 | background-color: rgba(0, 0, 0, 0.5);
173 | overflow: hidden;
174 | }
175 |
176 | .viewer-list {
177 | -webkit-box-sizing: content-box;
178 | box-sizing: content-box;
179 | height: 50px;
180 | margin: 0;
181 | overflow: hidden;
182 | padding: 1px 0;
183 | }
184 |
185 | .viewer-list > li {
186 | color: transparent;
187 | cursor: pointer;
188 | float: left;
189 | font-size: 0;
190 | height: 50px;
191 | line-height: 0;
192 | opacity: 0.5;
193 | overflow: hidden;
194 | -webkit-transition: opacity 0.15s;
195 | transition: opacity 0.15s;
196 | width: 30px;
197 | }
198 |
199 | .viewer-list > li:hover {
200 | opacity: 0.75;
201 | }
202 |
203 | .viewer-list > li + li {
204 | margin-left: 1px;
205 | }
206 |
207 | .viewer-list > .viewer-loading {
208 | position: relative;
209 | }
210 |
211 | .viewer-list > .viewer-loading::after {
212 | border-width: 2px;
213 | height: 20px;
214 | margin-left: -10px;
215 | margin-top: -10px;
216 | width: 20px;
217 | }
218 |
219 | .viewer-list > .viewer-active,
220 | .viewer-list > .viewer-active:hover {
221 | opacity: 1;
222 | }
223 |
224 | .viewer-player {
225 | background-color: #000;
226 | bottom: 0;
227 | cursor: none;
228 | display: none;
229 | left: 0;
230 | position: absolute;
231 | right: 0;
232 | top: 0;
233 | }
234 |
235 | .viewer-player > img {
236 | left: 0;
237 | position: absolute;
238 | top: 0;
239 | }
240 |
241 | .viewer-toolbar > ul {
242 | display: inline-block;
243 | margin: 0 auto 5px;
244 | overflow: hidden;
245 | padding: 3px 0;
246 | }
247 |
248 | .viewer-toolbar > ul > li {
249 | background-color: rgba(0, 0, 0, 0.5);
250 | border-radius: 50%;
251 | cursor: pointer;
252 | float: left;
253 | height: 24px;
254 | overflow: hidden;
255 | -webkit-transition: background-color 0.15s;
256 | transition: background-color 0.15s;
257 | width: 24px;
258 | }
259 |
260 | .viewer-toolbar > ul > li:hover {
261 | background-color: rgba(0, 0, 0, 0.8);
262 | }
263 |
264 | .viewer-toolbar > ul > li::before {
265 | margin: 2px;
266 | }
267 |
268 | .viewer-toolbar > ul > li + li {
269 | margin-left: 1px;
270 | }
271 |
272 | .viewer-toolbar > ul > .viewer-small {
273 | height: 18px;
274 | margin-bottom: 3px;
275 | margin-top: 3px;
276 | width: 18px;
277 | }
278 |
279 | .viewer-toolbar > ul > .viewer-small::before {
280 | margin: -1px;
281 | }
282 |
283 | .viewer-toolbar > ul > .viewer-large {
284 | height: 30px;
285 | margin-bottom: -3px;
286 | margin-top: -3px;
287 | width: 30px;
288 | }
289 |
290 | .viewer-toolbar > ul > .viewer-large::before {
291 | margin: 5px;
292 | }
293 |
294 | .viewer-tooltip {
295 | background-color: rgba(0, 0, 0, 0.8);
296 | border-radius: 10px;
297 | color: #fff;
298 | display: none;
299 | font-size: 12px;
300 | height: 20px;
301 | left: 50%;
302 | line-height: 20px;
303 | margin-left: -25px;
304 | margin-top: -10px;
305 | position: absolute;
306 | text-align: center;
307 | top: 50%;
308 | width: 50px;
309 | }
310 |
311 | .viewer-title {
312 | color: #ccc;
313 | display: inline-block;
314 | font-size: 12px;
315 | line-height: 1;
316 | margin: 0 5% 5px;
317 | max-width: 90%;
318 | opacity: 0.8;
319 | overflow: hidden;
320 | text-overflow: ellipsis;
321 | -webkit-transition: opacity 0.15s;
322 | transition: opacity 0.15s;
323 | white-space: nowrap;
324 | }
325 |
326 | .viewer-title:hover {
327 | opacity: 1;
328 | }
329 |
330 | .viewer-button {
331 | background-color: rgba(0, 0, 0, 0.5);
332 | border-radius: 50%;
333 | cursor: pointer;
334 | height: 80px;
335 | overflow: hidden;
336 | position: absolute;
337 | right: -40px;
338 | top: -40px;
339 | -webkit-transition: background-color 0.15s;
340 | transition: background-color 0.15s;
341 | width: 80px;
342 | }
343 |
344 | .viewer-button:focus,
345 | .viewer-button:hover {
346 | background-color: rgba(0, 0, 0, 0.8);
347 | }
348 |
349 | .viewer-button::before {
350 | bottom: 15px;
351 | left: 15px;
352 | position: absolute;
353 | }
354 |
355 | .viewer-fixed {
356 | position: fixed;
357 | }
358 |
359 | .viewer-open {
360 | overflow: hidden;
361 | }
362 |
363 | .viewer-show {
364 | display: block;
365 | }
366 |
367 | .viewer-hide {
368 | display: none;
369 | }
370 |
371 | .viewer-backdrop {
372 | background-color: rgba(0, 0, 0, 0.5);
373 | }
374 |
375 | .viewer-invisible {
376 | visibility: hidden;
377 | }
378 |
379 | .viewer-move {
380 | cursor: move;
381 | cursor: -webkit-grab;
382 | cursor: grab;
383 | }
384 |
385 | .viewer-fade {
386 | opacity: 0;
387 | }
388 |
389 | .viewer-in {
390 | opacity: 1;
391 | }
392 |
393 | .viewer-transition {
394 | -webkit-transition: all 0.3s;
395 | transition: all 0.3s;
396 | }
397 |
398 | @-webkit-keyframes viewer-spinner {
399 | 0% {
400 | -webkit-transform: rotate(0deg);
401 | transform: rotate(0deg);
402 | }
403 |
404 | 100% {
405 | -webkit-transform: rotate(360deg);
406 | transform: rotate(360deg);
407 | }
408 | }
409 |
410 | @keyframes viewer-spinner {
411 | 0% {
412 | -webkit-transform: rotate(0deg);
413 | transform: rotate(0deg);
414 | }
415 |
416 | 100% {
417 | -webkit-transform: rotate(360deg);
418 | transform: rotate(360deg);
419 | }
420 | }
421 |
422 | .viewer-loading::after {
423 | -webkit-animation: viewer-spinner 1s linear infinite;
424 | animation: viewer-spinner 1s linear infinite;
425 | border: 4px solid rgba(255, 255, 255, 0.1);
426 | border-left-color: rgba(255, 255, 255, 0.5);
427 | border-radius: 50%;
428 | content: '';
429 | display: inline-block;
430 | height: 40px;
431 | left: 50%;
432 | margin-left: -20px;
433 | margin-top: -20px;
434 | position: absolute;
435 | top: 50%;
436 | width: 40px;
437 | z-index: 1;
438 | }
439 |
440 | @media (max-width: 767px) {
441 | .viewer-hide-xs-down {
442 | display: none;
443 | }
444 | }
445 |
446 | @media (max-width: 991px) {
447 | .viewer-hide-sm-down {
448 | display: none;
449 | }
450 | }
451 |
452 | @media (max-width: 1199px) {
453 | .viewer-hide-md-down {
454 | display: none;
455 | }
456 | }
457 |
--------------------------------------------------------------------------------
/docs/css/viewer.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Viewer v1.1.0
3 | * https://fengyuanchen.github.io/viewer
4 | *
5 | * Copyright 2015-present Chen Fengyuan
6 | * Released under the MIT license
7 | *
8 | * Date: 2019-12-14T11:48:37.240Z
9 | */
10 |
11 | .viewer-zoom-in::before,
12 | .viewer-zoom-out::before,
13 | .viewer-one-to-one::before,
14 | .viewer-reset::before,
15 | .viewer-prev::before,
16 | .viewer-play::before,
17 | .viewer-next::before,
18 | .viewer-rotate-left::before,
19 | .viewer-rotate-right::before,
20 | .viewer-flip-horizontal::before,
21 | .viewer-flip-vertical::before,
22 | .viewer-fullscreen::before,
23 | .viewer-fullscreen-exit::before,
24 | .viewer-close::before {
25 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARgAAAAUCAYAAABWOyJDAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABx0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzIENTNui8sowAAAQPSURBVHic7Zs/iFxVFMa/0U2UaJGksUgnIVhYxVhpjDbZCBmLdAYECxsRFBTUamcXUiSNncgKQbSxsxH8gzAP3FU2jY0kKKJNiiiIghFlccnP4p3nPCdv3p9778vsLOcHB2bfveeb7955c3jvvNkBIMdxnD64a94GHMfZu3iBcRynN7zAOI7TG15gHCeeNUkr8zaxG2lbYDYsdgMbktBsP03jdQwljSXdtBhLOmtjowC9Mg9L+knSlcD8TNKpSA9lBpK2JF2VdDSR5n5J64m0qli399hNFMUlpshQii5jbXTbHGviB0nLNeNDSd9VO4A2UdB2fp+x0eCnaXxWXGA2X0au/3HgN9P4LFCjIANOJdrLr0zzZ+BEpNYDwKbpnQMeAw4m8HjQtM6Z9qa917zPQwFr3M5KgA6J5rTJCdFZJj9/lyvGhsDvwFNVuV2MhhjrK6b9bFiE+j1r87eBl4HDwCF7/U/k+ofAX5b/EXBv5JoLMuILzf3Ap6Z3EzgdqHMCuF7hcQf4HDgeoHnccncqdK/TvSDWffFXI/exICY/xZyqc6XLWF1UFZna4gJ7q8BsRvgd2/xXpo6P+D9dfT7PpECtA3cnWPM0GXGFZh/wgWltA+cDNC7X+AP4GzjZQe+k5dRxuYPeiuXU7e1qwLpDz7dFjXKRaSwuMLvAlG8zZlG+YmiK1HoFqT7wP2z+4Q45TfEGcMt01xLoNZEBTwRqD4BLpnMLeC1A41UmVxsXgXeBayV/Wx20rpTyrpnWRft7p6O/FdqzGrDukPNtkaMoMo3FBdBSQMOnYBCReyf05s126fU9ytfX98+mY54Kxnp7S9K3kj6U9KYdG0h6UdLbkh7poFXMfUnSOyVvL0h6VtIXHbS6nOP+s/Zm9mvyXW1uuC9ohZ72E9uDmXWLJOB1GxsH+DxPftsB8B6wlGDN02TAkxG6+4D3TWsbeC5CS8CDFce+AW500LhhOW2020TRjK3b21HEmgti9m0RonxbdMZeVzV+/4tF3cBpP7E9mKHNL5q8h5g0eYsCMQz0epq8gQrwMXAgcs0FGXGFRcB9wCemF9PkbYqM/Bas7fxLwNeJPdTdpo4itQti8lPMqTpXuozVRVXPpbHI3KkNTB1NfkL81j2mvhDp91HgV9MKuRIqrykj3WPq4rHyL+axj8/qGPmTqi6F9YDlHOvJU6oYcTsh/TYSzWmTE6JT19CtLTJt32D6CmHe0eQn1O8z5AXgT4sx4Vcu0/EQecMydB8z0hUWkTd2t4CrwNEePqMBcAR4mrBbwyXLPWJa8zrXmmLEhNBmfpkuY2102xxrih+pb+ieAb6vGhuA97UcJ5KR8gZ77K+99xxeYBzH6Q3/Z0fHcXrDC4zjOL3hBcZxnN74F+zlvXFWXF9PAAAAAElFTkSuQmCC');
26 | background-repeat: no-repeat;
27 | background-size: 280px;
28 | color: transparent;
29 | display: block;
30 | font-size: 0;
31 | height: 20px;
32 | line-height: 0;
33 | width: 20px;
34 | }
35 |
36 | .viewer-zoom-in::before {
37 | background-position: 0 0;
38 | content: 'Zoom In';
39 | }
40 |
41 | .viewer-zoom-out::before {
42 | background-position: -20px 0;
43 | content: 'Zoom Out';
44 | }
45 |
46 | .viewer-one-to-one::before {
47 | background-position: -40px 0;
48 | content: 'One to One';
49 | }
50 |
51 | .viewer-reset::before {
52 | background-position: -60px 0;
53 | content: 'Reset';
54 | }
55 |
56 | .viewer-prev::before {
57 | background-position: -80px 0;
58 | content: 'Previous';
59 | }
60 |
61 | .viewer-play::before {
62 | background-position: -100px 0;
63 | content: 'Play';
64 | }
65 |
66 | .viewer-next::before {
67 | background-position: -120px 0;
68 | content: 'Next';
69 | }
70 |
71 | .viewer-rotate-left::before {
72 | background-position: -140px 0;
73 | content: 'Rotate Left';
74 | }
75 |
76 | .viewer-rotate-right::before {
77 | background-position: -160px 0;
78 | content: 'Rotate Right';
79 | }
80 |
81 | .viewer-flip-horizontal::before {
82 | background-position: -180px 0;
83 | content: 'Flip Horizontal';
84 | }
85 |
86 | .viewer-flip-vertical::before {
87 | background-position: -200px 0;
88 | content: 'Flip Vertical';
89 | }
90 |
91 | .viewer-fullscreen::before {
92 | background-position: -220px 0;
93 | content: 'Enter Full Screen';
94 | }
95 |
96 | .viewer-fullscreen-exit::before {
97 | background-position: -240px 0;
98 | content: 'Exit Full Screen';
99 | }
100 |
101 | .viewer-close::before {
102 | background-position: -260px 0;
103 | content: 'Close';
104 | }
105 |
106 | .viewer-container {
107 | bottom: 0;
108 | direction: ltr;
109 | font-size: 0;
110 | left: 0;
111 | line-height: 0;
112 | overflow: hidden;
113 | position: absolute;
114 | right: 0;
115 | -webkit-tap-highlight-color: transparent;
116 | top: 0;
117 | -ms-touch-action: none;
118 | touch-action: none;
119 | -webkit-touch-callout: none;
120 | -webkit-user-select: none;
121 | -moz-user-select: none;
122 | -ms-user-select: none;
123 | user-select: none;
124 | }
125 |
126 | .viewer-container::-moz-selection,
127 | .viewer-container *::-moz-selection {
128 | background-color: transparent;
129 | }
130 |
131 | .viewer-container::selection,
132 | .viewer-container *::selection {
133 | background-color: transparent;
134 | }
135 |
136 | .viewer-container img {
137 | display: block;
138 | height: auto;
139 | max-height: none !important;
140 | max-width: none !important;
141 | min-height: 0 !important;
142 | min-width: 0 !important;
143 | width: 100%;
144 | }
145 |
146 | .viewer-canvas {
147 | bottom: 0;
148 | left: 0;
149 | overflow: hidden;
150 | position: absolute;
151 | right: 0;
152 | top: 0;
153 | }
154 |
155 | .viewer-canvas > img {
156 | height: auto;
157 | margin: 15px auto;
158 | max-width: 90% !important;
159 | width: auto;
160 | }
161 |
162 | .viewer-footer {
163 | bottom: 0;
164 | left: 0;
165 | overflow: hidden;
166 | position: absolute;
167 | right: 0;
168 | text-align: center;
169 | }
170 |
171 | .viewer-navbar {
172 | background-color: rgba(0, 0, 0, 0.5);
173 | overflow: hidden;
174 | }
175 |
176 | .viewer-list {
177 | -webkit-box-sizing: content-box;
178 | box-sizing: content-box;
179 | height: 50px;
180 | margin: 0;
181 | overflow: hidden;
182 | padding: 1px 0;
183 | }
184 |
185 | .viewer-list > li {
186 | color: transparent;
187 | cursor: pointer;
188 | float: left;
189 | font-size: 0;
190 | height: 50px;
191 | line-height: 0;
192 | opacity: 0.5;
193 | overflow: hidden;
194 | -webkit-transition: opacity 0.15s;
195 | transition: opacity 0.15s;
196 | width: 30px;
197 | }
198 |
199 | .viewer-list > li:hover {
200 | opacity: 0.75;
201 | }
202 |
203 | .viewer-list > li + li {
204 | margin-left: 1px;
205 | }
206 |
207 | .viewer-list > .viewer-loading {
208 | position: relative;
209 | }
210 |
211 | .viewer-list > .viewer-loading::after {
212 | border-width: 2px;
213 | height: 20px;
214 | margin-left: -10px;
215 | margin-top: -10px;
216 | width: 20px;
217 | }
218 |
219 | .viewer-list > .viewer-active,
220 | .viewer-list > .viewer-active:hover {
221 | opacity: 1;
222 | }
223 |
224 | .viewer-player {
225 | background-color: #000;
226 | bottom: 0;
227 | cursor: none;
228 | display: none;
229 | left: 0;
230 | position: absolute;
231 | right: 0;
232 | top: 0;
233 | }
234 |
235 | .viewer-player > img {
236 | left: 0;
237 | position: absolute;
238 | top: 0;
239 | }
240 |
241 | .viewer-toolbar > ul {
242 | display: inline-block;
243 | margin: 0 auto 5px;
244 | overflow: hidden;
245 | padding: 3px 0;
246 | }
247 |
248 | .viewer-toolbar > ul > li {
249 | background-color: rgba(0, 0, 0, 0.5);
250 | border-radius: 50%;
251 | cursor: pointer;
252 | float: left;
253 | height: 24px;
254 | overflow: hidden;
255 | -webkit-transition: background-color 0.15s;
256 | transition: background-color 0.15s;
257 | width: 24px;
258 | }
259 |
260 | .viewer-toolbar > ul > li:hover {
261 | background-color: rgba(0, 0, 0, 0.8);
262 | }
263 |
264 | .viewer-toolbar > ul > li::before {
265 | margin: 2px;
266 | }
267 |
268 | .viewer-toolbar > ul > li + li {
269 | margin-left: 1px;
270 | }
271 |
272 | .viewer-toolbar > ul > .viewer-small {
273 | height: 18px;
274 | margin-bottom: 3px;
275 | margin-top: 3px;
276 | width: 18px;
277 | }
278 |
279 | .viewer-toolbar > ul > .viewer-small::before {
280 | margin: -1px;
281 | }
282 |
283 | .viewer-toolbar > ul > .viewer-large {
284 | height: 30px;
285 | margin-bottom: -3px;
286 | margin-top: -3px;
287 | width: 30px;
288 | }
289 |
290 | .viewer-toolbar > ul > .viewer-large::before {
291 | margin: 5px;
292 | }
293 |
294 | .viewer-tooltip {
295 | background-color: rgba(0, 0, 0, 0.8);
296 | border-radius: 10px;
297 | color: #fff;
298 | display: none;
299 | font-size: 12px;
300 | height: 20px;
301 | left: 50%;
302 | line-height: 20px;
303 | margin-left: -25px;
304 | margin-top: -10px;
305 | position: absolute;
306 | text-align: center;
307 | top: 50%;
308 | width: 50px;
309 | }
310 |
311 | .viewer-title {
312 | color: #ccc;
313 | display: inline-block;
314 | font-size: 12px;
315 | line-height: 1;
316 | margin: 0 5% 5px;
317 | max-width: 90%;
318 | opacity: 0.8;
319 | overflow: hidden;
320 | text-overflow: ellipsis;
321 | -webkit-transition: opacity 0.15s;
322 | transition: opacity 0.15s;
323 | white-space: nowrap;
324 | }
325 |
326 | .viewer-title:hover {
327 | opacity: 1;
328 | }
329 |
330 | .viewer-button {
331 | background-color: rgba(0, 0, 0, 0.5);
332 | border-radius: 50%;
333 | cursor: pointer;
334 | height: 80px;
335 | overflow: hidden;
336 | position: absolute;
337 | right: -40px;
338 | top: -40px;
339 | -webkit-transition: background-color 0.15s;
340 | transition: background-color 0.15s;
341 | width: 80px;
342 | }
343 |
344 | .viewer-button:focus,
345 | .viewer-button:hover {
346 | background-color: rgba(0, 0, 0, 0.8);
347 | }
348 |
349 | .viewer-button::before {
350 | bottom: 15px;
351 | left: 15px;
352 | position: absolute;
353 | }
354 |
355 | .viewer-fixed {
356 | position: fixed;
357 | }
358 |
359 | .viewer-open {
360 | overflow: hidden;
361 | }
362 |
363 | .viewer-show {
364 | display: block;
365 | }
366 |
367 | .viewer-hide {
368 | display: none;
369 | }
370 |
371 | .viewer-backdrop {
372 | background-color: rgba(0, 0, 0, 0.5);
373 | }
374 |
375 | .viewer-invisible {
376 | visibility: hidden;
377 | }
378 |
379 | .viewer-move {
380 | cursor: move;
381 | cursor: -webkit-grab;
382 | cursor: grab;
383 | }
384 |
385 | .viewer-fade {
386 | opacity: 0;
387 | }
388 |
389 | .viewer-in {
390 | opacity: 1;
391 | }
392 |
393 | .viewer-transition {
394 | -webkit-transition: all 0.3s;
395 | transition: all 0.3s;
396 | }
397 |
398 | @-webkit-keyframes viewer-spinner {
399 | 0% {
400 | -webkit-transform: rotate(0deg);
401 | transform: rotate(0deg);
402 | }
403 |
404 | 100% {
405 | -webkit-transform: rotate(360deg);
406 | transform: rotate(360deg);
407 | }
408 | }
409 |
410 | @keyframes viewer-spinner {
411 | 0% {
412 | -webkit-transform: rotate(0deg);
413 | transform: rotate(0deg);
414 | }
415 |
416 | 100% {
417 | -webkit-transform: rotate(360deg);
418 | transform: rotate(360deg);
419 | }
420 | }
421 |
422 | .viewer-loading::after {
423 | -webkit-animation: viewer-spinner 1s linear infinite;
424 | animation: viewer-spinner 1s linear infinite;
425 | border: 4px solid rgba(255, 255, 255, 0.1);
426 | border-left-color: rgba(255, 255, 255, 0.5);
427 | border-radius: 50%;
428 | content: '';
429 | display: inline-block;
430 | height: 40px;
431 | left: 50%;
432 | margin-left: -20px;
433 | margin-top: -20px;
434 | position: absolute;
435 | top: 50%;
436 | width: 40px;
437 | z-index: 1;
438 | }
439 |
440 | @media (max-width: 767px) {
441 | .viewer-hide-xs-down {
442 | display: none;
443 | }
444 | }
445 |
446 | @media (max-width: 991px) {
447 | .viewer-hide-sm-down {
448 | display: none;
449 | }
450 | }
451 |
452 | @media (max-width: 1199px) {
453 | .viewer-hide-md-down {
454 | display: none;
455 | }
456 | }
457 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Viewer
10 |
11 |
12 |
13 |
14 |
15 |
23 |
24 |
25 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
Viewer v1.1.0
49 |
A simple jQuery image viewing plugin.
50 |
51 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
Overview
63 |
64 |
65 |
66 |
Options
67 |
68 |
197 |
198 |
199 |
Demo
200 |
201 |
214 |
215 |
216 |
Methods
217 |
218 |
285 |
286 |
287 |
288 |
289 |
290 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
--------------------------------------------------------------------------------
/dist/viewer.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Viewer v1.1.0
3 | * https://fengyuanchen.github.io/viewer
4 | *
5 | * Copyright 2015-present Chen Fengyuan
6 | * Released under the MIT license
7 | *
8 | * Date: 2019-12-14T11:48:41.205Z
9 | */
10 | !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],i):i((t=t||self).jQuery)}(this,function(c){"use strict";function i(t){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function s(t,i){for(var e=0;e=this.length||this.viewed&&i===this.index)return this;if(!this.isShown)return this.index=i,this.show();this.viewing&&this.viewing.abort();var e=this.element,s=this.options,o=this.title,a=this.canvas,r=this.items[i],h=r.querySelector("img"),l=ft(h,"originalUrl"),c=h.getAttribute("alt"),u=document.createElement("img");if(u.src=l,u.alt=c,nt(s.view)&&wt(e,"view",s.view,{once:!0}),!1===bt(e,"view",{originalImage:this.images[i],index:i,image:u})||!this.isShown||this.hiding||this.played)return this;this.image=u,ct(this.items[this.index],g),lt(r,g),this.viewed=!1,this.index=i,this.imageData={},lt(u,T),s.loading&<(a,E),a.innerHTML="",a.appendChild(u),this.renderList(),o.innerHTML="";function d(){var t,i=n.imageData,e=Array.isArray(s.title)?s.title[1]:s.title;o.innerHTML=_(t=nt(e)?e.call(n,u,i):"".concat(c," (").concat(i.naturalWidth," × ").concat(i.naturalHeight,")"))?t.replace(/&(?!amp;|quot;|#39;|lt;|gt;)/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">"):t}var m;return wt(e,K,d,{once:!0}),this.viewing={abort:function(){pt(e,K,d),u.complete?this.imageRendering?this.imageRendering.abort():this.imageInitializing&&this.imageInitializing.abort():(u.src="",pt(u,X,m),this.timeout&&clearTimeout(this.timeout))}},u.complete?this.load():(wt(u,X,m=this.load.bind(this),{once:!0}),this.timeout&&clearTimeout(this.timeout),this.timeout=setTimeout(function(){ct(u,T),n.timeout=!1},1e3)),this},prev:function(t){var i=0Math.abs(r)&&(this.pointers={},1';var n=e.querySelector(".".concat(p,"-container")),s=n.querySelector(".".concat(p,"-title")),o=n.querySelector(".".concat(p,"-toolbar")),a=n.querySelector(".".concat(p,"-navbar")),r=n.querySelector(".".concat(p,"-button")),l=n.querySelector(".".concat(p,"-canvas"));if(this.parent=i,this.viewer=n,this.title=s,this.toolbar=o,this.navbar=a,this.button=r,this.canvas=l,this.footer=n.querySelector(".".concat(p,"-footer")),this.tooltipBox=n.querySelector(".".concat(p,"-tooltip")),this.player=n.querySelector(".".concat(p,"-player")),this.list=n.querySelector(".".concat(p,"-list")),lt(s,h.title?zt(Array.isArray(h.title)?h.title[0]:h.title):k),lt(a,h.navbar?zt(h.navbar):k),ut(r,k,!h.button),h.backdrop&&(lt(n,"".concat(p,"-backdrop")),h.inline||"static"===h.backdrop||gt(l,U,"hide")),_(h.className)&&h.className&&h.className.split(Z).forEach(function(t){lt(n,t)}),h.toolbar){var c=document.createElement("ul"),u=et(h.toolbar),d=$.slice(0,3),m=$.slice(7,9),f=$.slice(9);u||lt(o,zt(h.toolbar)),st(u?h.toolbar:$,function(t,i){var e=u&&et(t),n=u?mt(i):t,s=e&&!J(t.show)?t.show:t;if(s&&(h.zoomable||-1===d.indexOf(n))&&(h.rotatable||-1===m.indexOf(n))&&(h.scalable||-1===f.indexOf(n))){var o=e&&!J(t.size)?t.size:t,a=e&&!J(t.click)?t.click:t,r=document.createElement("li");r.setAttribute("role","button"),lt(r,"".concat(p,"-").concat(n)),nt(a)||gt(r,U,n),G(s)&<(r,zt(s)),-1!==["small","large"].indexOf(o)?lt(r,"".concat(p,"-").concat(o)):"play"===n&<(r,"".concat(p,"-large")),nt(a)&&wt(r,L,a),c.appendChild(r)}}),o.appendChild(c)}else lt(o,k);if(!h.rotatable){var g=o.querySelectorAll('li[class*="rotate"]');lt(g,T),st(g,function(t){o.appendChild(t)})}if(h.inline)lt(r,x),rt(n,{zIndex:h.zIndexInline}),"static"===window.getComputedStyle(i).position&&rt(i,{position:"relative"}),i.insertBefore(n,t.nextSibling);else{lt(r,w),lt(n,y),lt(n,b),lt(n,k),rt(n,{zIndex:h.zIndex});var v=h.container;_(v)&&(v=t.ownerDocument.querySelector(v)),(v=v||this.body).appendChild(n)}h.inline&&(this.render(),this.bind(),this.isShown=!0),this.ready=!0,nt(h.ready)&&wt(t,W,h.ready,{once:!0}),!1!==bt(t,W)?this.ready&&h.inline&&this.view(this.index):this.ready=!1}}}])&&s(t.prototype,i),n&&s(t,n),e}();if(ot(Lt.prototype,Tt,Et,It,Ot,St),c.fn){var Nt=c.fn.viewer,qt="viewer";c.fn.viewer=function(r){for(var t=arguments.length,h=new Array(1' + '' + '' + '' + '' + '';
315 |
316 | var IS_BROWSER = typeof window !== 'undefined' && typeof window.document !== 'undefined';
317 | var WINDOW = IS_BROWSER ? window : {};
318 | var IS_TOUCH_DEVICE = IS_BROWSER ? 'ontouchstart' in WINDOW.document.documentElement : false;
319 | var HAS_POINTER_EVENT = IS_BROWSER ? 'PointerEvent' in WINDOW : false;
320 | var NAMESPACE = 'viewer'; // Actions
321 |
322 | var ACTION_MOVE = 'move';
323 | var ACTION_SWITCH = 'switch';
324 | var ACTION_ZOOM = 'zoom'; // Classes
325 |
326 | var CLASS_ACTIVE = "".concat(NAMESPACE, "-active");
327 | var CLASS_CLOSE = "".concat(NAMESPACE, "-close");
328 | var CLASS_FADE = "".concat(NAMESPACE, "-fade");
329 | var CLASS_FIXED = "".concat(NAMESPACE, "-fixed");
330 | var CLASS_FULLSCREEN = "".concat(NAMESPACE, "-fullscreen");
331 | var CLASS_FULLSCREEN_EXIT = "".concat(NAMESPACE, "-fullscreen-exit");
332 | var CLASS_HIDE = "".concat(NAMESPACE, "-hide");
333 | var CLASS_HIDE_MD_DOWN = "".concat(NAMESPACE, "-hide-md-down");
334 | var CLASS_HIDE_SM_DOWN = "".concat(NAMESPACE, "-hide-sm-down");
335 | var CLASS_HIDE_XS_DOWN = "".concat(NAMESPACE, "-hide-xs-down");
336 | var CLASS_IN = "".concat(NAMESPACE, "-in");
337 | var CLASS_INVISIBLE = "".concat(NAMESPACE, "-invisible");
338 | var CLASS_LOADING = "".concat(NAMESPACE, "-loading");
339 | var CLASS_MOVE = "".concat(NAMESPACE, "-move");
340 | var CLASS_OPEN = "".concat(NAMESPACE, "-open");
341 | var CLASS_SHOW = "".concat(NAMESPACE, "-show");
342 | var CLASS_TRANSITION = "".concat(NAMESPACE, "-transition"); // Events
343 |
344 | var EVENT_CLICK = 'click';
345 | var EVENT_DBLCLICK = 'dblclick';
346 | var EVENT_DRAG_START = 'dragstart';
347 | var EVENT_HIDDEN = 'hidden';
348 | var EVENT_HIDE = 'hide';
349 | var EVENT_KEY_DOWN = 'keydown';
350 | var EVENT_LOAD = 'load';
351 | var EVENT_TOUCH_START = IS_TOUCH_DEVICE ? 'touchstart' : 'mousedown';
352 | var EVENT_TOUCH_MOVE = IS_TOUCH_DEVICE ? 'touchmove' : 'mousemove';
353 | var EVENT_TOUCH_END = IS_TOUCH_DEVICE ? 'touchend touchcancel' : 'mouseup';
354 | var EVENT_POINTER_DOWN = HAS_POINTER_EVENT ? 'pointerdown' : EVENT_TOUCH_START;
355 | var EVENT_POINTER_MOVE = HAS_POINTER_EVENT ? 'pointermove' : EVENT_TOUCH_MOVE;
356 | var EVENT_POINTER_UP = HAS_POINTER_EVENT ? 'pointerup pointercancel' : EVENT_TOUCH_END;
357 | var EVENT_READY = 'ready';
358 | var EVENT_RESIZE = 'resize';
359 | var EVENT_SHOW = 'show';
360 | var EVENT_SHOWN = 'shown';
361 | var EVENT_TRANSITION_END = 'transitionend';
362 | var EVENT_VIEW = 'view';
363 | var EVENT_VIEWED = 'viewed';
364 | var EVENT_WHEEL = 'wheel';
365 | var EVENT_ZOOM = 'zoom';
366 | var EVENT_ZOOMED = 'zoomed'; // Data keys
367 |
368 | var DATA_ACTION = "".concat(NAMESPACE, "Action"); // RegExps
369 |
370 | var REGEXP_SPACES = /\s\s*/; // Misc
371 |
372 | var BUTTONS = ['zoom-in', 'zoom-out', 'one-to-one', 'reset', 'prev', 'play', 'next', 'rotate-left', 'rotate-right', 'flip-horizontal', 'flip-vertical'];
373 |
374 | /**
375 | * Check if the given value is a string.
376 | * @param {*} value - The value to check.
377 | * @returns {boolean} Returns `true` if the given value is a string, else `false`.
378 | */
379 |
380 | function isString(value) {
381 | return typeof value === 'string';
382 | }
383 | /**
384 | * Check if the given value is not a number.
385 | */
386 |
387 | var isNaN = Number.isNaN || WINDOW.isNaN;
388 | /**
389 | * Check if the given value is a number.
390 | * @param {*} value - The value to check.
391 | * @returns {boolean} Returns `true` if the given value is a number, else `false`.
392 | */
393 |
394 | function isNumber(value) {
395 | return typeof value === 'number' && !isNaN(value);
396 | }
397 | /**
398 | * Check if the given value is undefined.
399 | * @param {*} value - The value to check.
400 | * @returns {boolean} Returns `true` if the given value is undefined, else `false`.
401 | */
402 |
403 | function isUndefined(value) {
404 | return typeof value === 'undefined';
405 | }
406 | /**
407 | * Check if the given value is an object.
408 | * @param {*} value - The value to check.
409 | * @returns {boolean} Returns `true` if the given value is an object, else `false`.
410 | */
411 |
412 | function isObject(value) {
413 | return _typeof(value) === 'object' && value !== null;
414 | }
415 | var hasOwnProperty = Object.prototype.hasOwnProperty;
416 | /**
417 | * Check if the given value is a plain object.
418 | * @param {*} value - The value to check.
419 | * @returns {boolean} Returns `true` if the given value is a plain object, else `false`.
420 | */
421 |
422 | function isPlainObject(value) {
423 | if (!isObject(value)) {
424 | return false;
425 | }
426 |
427 | try {
428 | var _constructor = value.constructor;
429 | var prototype = _constructor.prototype;
430 | return _constructor && prototype && hasOwnProperty.call(prototype, 'isPrototypeOf');
431 | } catch (error) {
432 | return false;
433 | }
434 | }
435 | /**
436 | * Check if the given value is a function.
437 | * @param {*} value - The value to check.
438 | * @returns {boolean} Returns `true` if the given value is a function, else `false`.
439 | */
440 |
441 | function isFunction(value) {
442 | return typeof value === 'function';
443 | }
444 | /**
445 | * Iterate the given data.
446 | * @param {*} data - The data to iterate.
447 | * @param {Function} callback - The process function for each element.
448 | * @returns {*} The original data.
449 | */
450 |
451 | function forEach(data, callback) {
452 | if (data && isFunction(callback)) {
453 | if (Array.isArray(data) || isNumber(data.length)
454 | /* array-like */
455 | ) {
456 | var length = data.length;
457 | var i;
458 |
459 | for (i = 0; i < length; i += 1) {
460 | if (callback.call(data, data[i], i, data) === false) {
461 | break;
462 | }
463 | }
464 | } else if (isObject(data)) {
465 | Object.keys(data).forEach(function (key) {
466 | callback.call(data, data[key], key, data);
467 | });
468 | }
469 | }
470 |
471 | return data;
472 | }
473 | /**
474 | * Extend the given object.
475 | * @param {*} obj - The object to be extended.
476 | * @param {*} args - The rest objects which will be merged to the first object.
477 | * @returns {Object} The extended object.
478 | */
479 |
480 | var assign = Object.assign || function assign(obj) {
481 | for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
482 | args[_key - 1] = arguments[_key];
483 | }
484 |
485 | if (isObject(obj) && args.length > 0) {
486 | args.forEach(function (arg) {
487 | if (isObject(arg)) {
488 | Object.keys(arg).forEach(function (key) {
489 | obj[key] = arg[key];
490 | });
491 | }
492 | });
493 | }
494 |
495 | return obj;
496 | };
497 | var REGEXP_SUFFIX = /^(?:width|height|left|top|marginLeft|marginTop)$/;
498 | /**
499 | * Apply styles to the given element.
500 | * @param {Element} element - The target element.
501 | * @param {Object} styles - The styles for applying.
502 | */
503 |
504 | function setStyle(element, styles) {
505 | var style = element.style;
506 | forEach(styles, function (value, property) {
507 | if (REGEXP_SUFFIX.test(property) && isNumber(value)) {
508 | value += 'px';
509 | }
510 |
511 | style[property] = value;
512 | });
513 | }
514 | /**
515 | * Escape a string for using in HTML.
516 | * @param {String} value - The string to escape.
517 | * @returns {String} Returns the escaped string.
518 | */
519 |
520 | function escapeHTMLEntities(value) {
521 | return isString(value) ? value.replace(/&(?!amp;|quot;|#39;|lt;|gt;)/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(//g, '>') : value;
522 | }
523 | /**
524 | * Check if the given element has a special class.
525 | * @param {Element} element - The element to check.
526 | * @param {string} value - The class to search.
527 | * @returns {boolean} Returns `true` if the special class was found.
528 | */
529 |
530 | function hasClass(element, value) {
531 | if (!element || !value) {
532 | return false;
533 | }
534 |
535 | return element.classList ? element.classList.contains(value) : element.className.indexOf(value) > -1;
536 | }
537 | /**
538 | * Add classes to the given element.
539 | * @param {Element} element - The target element.
540 | * @param {string} value - The classes to be added.
541 | */
542 |
543 | function addClass(element, value) {
544 | if (!element || !value) {
545 | return;
546 | }
547 |
548 | if (isNumber(element.length)) {
549 | forEach(element, function (elem) {
550 | addClass(elem, value);
551 | });
552 | return;
553 | }
554 |
555 | if (element.classList) {
556 | element.classList.add(value);
557 | return;
558 | }
559 |
560 | var className = element.className.trim();
561 |
562 | if (!className) {
563 | element.className = value;
564 | } else if (className.indexOf(value) < 0) {
565 | element.className = "".concat(className, " ").concat(value);
566 | }
567 | }
568 | /**
569 | * Remove classes from the given element.
570 | * @param {Element} element - The target element.
571 | * @param {string} value - The classes to be removed.
572 | */
573 |
574 | function removeClass(element, value) {
575 | if (!element || !value) {
576 | return;
577 | }
578 |
579 | if (isNumber(element.length)) {
580 | forEach(element, function (elem) {
581 | removeClass(elem, value);
582 | });
583 | return;
584 | }
585 |
586 | if (element.classList) {
587 | element.classList.remove(value);
588 | return;
589 | }
590 |
591 | if (element.className.indexOf(value) >= 0) {
592 | element.className = element.className.replace(value, '');
593 | }
594 | }
595 | /**
596 | * Add or remove classes from the given element.
597 | * @param {Element} element - The target element.
598 | * @param {string} value - The classes to be toggled.
599 | * @param {boolean} added - Add only.
600 | */
601 |
602 | function toggleClass(element, value, added) {
603 | if (!value) {
604 | return;
605 | }
606 |
607 | if (isNumber(element.length)) {
608 | forEach(element, function (elem) {
609 | toggleClass(elem, value, added);
610 | });
611 | return;
612 | } // IE10-11 doesn't support the second parameter of `classList.toggle`
613 |
614 |
615 | if (added) {
616 | addClass(element, value);
617 | } else {
618 | removeClass(element, value);
619 | }
620 | }
621 | var REGEXP_HYPHENATE = /([a-z\d])([A-Z])/g;
622 | /**
623 | * Transform the given string from camelCase to kebab-case
624 | * @param {string} value - The value to transform.
625 | * @returns {string} The transformed value.
626 | */
627 |
628 | function hyphenate(value) {
629 | return value.replace(REGEXP_HYPHENATE, '$1-$2').toLowerCase();
630 | }
631 | /**
632 | * Get data from the given element.
633 | * @param {Element} element - The target element.
634 | * @param {string} name - The data key to get.
635 | * @returns {string} The data value.
636 | */
637 |
638 | function getData(element, name) {
639 | if (isObject(element[name])) {
640 | return element[name];
641 | }
642 |
643 | if (element.dataset) {
644 | return element.dataset[name];
645 | }
646 |
647 | return element.getAttribute("data-".concat(hyphenate(name)));
648 | }
649 | /**
650 | * Set data to the given element.
651 | * @param {Element} element - The target element.
652 | * @param {string} name - The data key to set.
653 | * @param {string} data - The data value.
654 | */
655 |
656 | function setData(element, name, data) {
657 | if (isObject(data)) {
658 | element[name] = data;
659 | } else if (element.dataset) {
660 | element.dataset[name] = data;
661 | } else {
662 | element.setAttribute("data-".concat(hyphenate(name)), data);
663 | }
664 | }
665 |
666 | var onceSupported = function () {
667 | var supported = false;
668 |
669 | if (IS_BROWSER) {
670 | var once = false;
671 |
672 | var listener = function listener() {};
673 |
674 | var options = Object.defineProperty({}, 'once', {
675 | get: function get() {
676 | supported = true;
677 | return once;
678 | },
679 |
680 | /**
681 | * This setter can fix a `TypeError` in strict mode
682 | * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Getter_only}
683 | * @param {boolean} value - The value to set
684 | */
685 | set: function set(value) {
686 | once = value;
687 | }
688 | });
689 | WINDOW.addEventListener('test', listener, options);
690 | WINDOW.removeEventListener('test', listener, options);
691 | }
692 |
693 | return supported;
694 | }();
695 | /**
696 | * Remove event listener from the target element.
697 | * @param {Element} element - The event target.
698 | * @param {string} type - The event type(s).
699 | * @param {Function} listener - The event listener.
700 | * @param {Object} options - The event options.
701 | */
702 |
703 |
704 | function removeListener(element, type, listener) {
705 | var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
706 | var handler = listener;
707 | type.trim().split(REGEXP_SPACES).forEach(function (event) {
708 | if (!onceSupported) {
709 | var listeners = element.listeners;
710 |
711 | if (listeners && listeners[event] && listeners[event][listener]) {
712 | handler = listeners[event][listener];
713 | delete listeners[event][listener];
714 |
715 | if (Object.keys(listeners[event]).length === 0) {
716 | delete listeners[event];
717 | }
718 |
719 | if (Object.keys(listeners).length === 0) {
720 | delete element.listeners;
721 | }
722 | }
723 | }
724 |
725 | element.removeEventListener(event, handler, options);
726 | });
727 | }
728 | /**
729 | * Add event listener to the target element.
730 | * @param {Element} element - The event target.
731 | * @param {string} type - The event type(s).
732 | * @param {Function} listener - The event listener.
733 | * @param {Object} options - The event options.
734 | */
735 |
736 | function addListener(element, type, listener) {
737 | var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
738 | var _handler = listener;
739 | type.trim().split(REGEXP_SPACES).forEach(function (event) {
740 | if (options.once && !onceSupported) {
741 | var _element$listeners = element.listeners,
742 | listeners = _element$listeners === void 0 ? {} : _element$listeners;
743 |
744 | _handler = function handler() {
745 | delete listeners[event][listener];
746 | element.removeEventListener(event, _handler, options);
747 |
748 | for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
749 | args[_key2] = arguments[_key2];
750 | }
751 |
752 | listener.apply(element, args);
753 | };
754 |
755 | if (!listeners[event]) {
756 | listeners[event] = {};
757 | }
758 |
759 | if (listeners[event][listener]) {
760 | element.removeEventListener(event, listeners[event][listener], options);
761 | }
762 |
763 | listeners[event][listener] = _handler;
764 | element.listeners = listeners;
765 | }
766 |
767 | element.addEventListener(event, _handler, options);
768 | });
769 | }
770 | /**
771 | * Dispatch event on the target element.
772 | * @param {Element} element - The event target.
773 | * @param {string} type - The event type(s).
774 | * @param {Object} data - The additional event data.
775 | * @returns {boolean} Indicate if the event is default prevented or not.
776 | */
777 |
778 | function dispatchEvent(element, type, data) {
779 | var event; // Event and CustomEvent on IE9-11 are global objects, not constructors
780 |
781 | if (isFunction(Event) && isFunction(CustomEvent)) {
782 | event = new CustomEvent(type, {
783 | detail: data,
784 | bubbles: true,
785 | cancelable: true
786 | });
787 | } else {
788 | event = document.createEvent('CustomEvent');
789 | event.initCustomEvent(type, true, true, data);
790 | }
791 |
792 | return element.dispatchEvent(event);
793 | }
794 | /**
795 | * Get the offset base on the document.
796 | * @param {Element} element - The target element.
797 | * @returns {Object} The offset data.
798 | */
799 |
800 | function getOffset(element) {
801 | var box = element.getBoundingClientRect();
802 | return {
803 | left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
804 | top: box.top + (window.pageYOffset - document.documentElement.clientTop)
805 | };
806 | }
807 | /**
808 | * Get transforms base on the given object.
809 | * @param {Object} obj - The target object.
810 | * @returns {string} A string contains transform values.
811 | */
812 |
813 | function getTransforms(_ref) {
814 | var rotate = _ref.rotate,
815 | scaleX = _ref.scaleX,
816 | scaleY = _ref.scaleY,
817 | translateX = _ref.translateX,
818 | translateY = _ref.translateY;
819 | var values = [];
820 |
821 | if (isNumber(translateX) && translateX !== 0) {
822 | values.push("translateX(".concat(translateX, "px)"));
823 | }
824 |
825 | if (isNumber(translateY) && translateY !== 0) {
826 | values.push("translateY(".concat(translateY, "px)"));
827 | } // Rotate should come first before scale to match orientation transform
828 |
829 |
830 | if (isNumber(rotate) && rotate !== 0) {
831 | values.push("rotate(".concat(rotate, "deg)"));
832 | }
833 |
834 | if (isNumber(scaleX) && scaleX !== 1) {
835 | values.push("scaleX(".concat(scaleX, ")"));
836 | }
837 |
838 | if (isNumber(scaleY) && scaleY !== 1) {
839 | values.push("scaleY(".concat(scaleY, ")"));
840 | }
841 |
842 | var transform = values.length ? values.join(' ') : 'none';
843 | return {
844 | WebkitTransform: transform,
845 | msTransform: transform,
846 | transform: transform
847 | };
848 | }
849 | /**
850 | * Get an image name from an image url.
851 | * @param {string} url - The target url.
852 | * @example
853 | * // picture.jpg
854 | * getImageNameFromURL('https://domain.com/path/to/picture.jpg?size=1280×960')
855 | * @returns {string} A string contains the image name.
856 | */
857 |
858 | function getImageNameFromURL(url) {
859 | return isString(url) ? decodeURIComponent(url.replace(/^.*\//, '').replace(/[?].*$/, '')) : '';
860 | }
861 | var IS_SAFARI = WINDOW.navigator && /(Macintosh|iPhone|iPod|iPad).*AppleWebKit/i.test(WINDOW.navigator.userAgent);
862 | /**
863 | * Get an image's natural sizes.
864 | * @param {string} image - The target image.
865 | * @param {Function} callback - The callback function.
866 | * @returns {HTMLImageElement} The new image.
867 | */
868 |
869 | function getImageNaturalSizes(image, callback) {
870 | var newImage = document.createElement('img'); // Modern browsers (except Safari)
871 |
872 | if (image.naturalWidth && !IS_SAFARI) {
873 | callback(image.naturalWidth, image.naturalHeight);
874 | return newImage;
875 | }
876 |
877 | var body = document.body || document.documentElement;
878 |
879 | newImage.onload = function () {
880 | callback(newImage.width, newImage.height);
881 |
882 | if (!IS_SAFARI) {
883 | body.removeChild(newImage);
884 | }
885 | };
886 |
887 | newImage.src = image.src; // iOS Safari will convert the image automatically
888 | // with its orientation once append it into DOM
889 |
890 | if (!IS_SAFARI) {
891 | newImage.style.cssText = 'left:0;' + 'max-height:none!important;' + 'max-width:none!important;' + 'min-height:0!important;' + 'min-width:0!important;' + 'opacity:0;' + 'position:absolute;' + 'top:0;' + 'z-index:-1;';
892 | body.appendChild(newImage);
893 | }
894 |
895 | return newImage;
896 | }
897 | /**
898 | * Get the related class name of a responsive type number.
899 | * @param {string} type - The responsive type.
900 | * @returns {string} The related class name.
901 | */
902 |
903 | function getResponsiveClass(type) {
904 | switch (type) {
905 | case 2:
906 | return CLASS_HIDE_XS_DOWN;
907 |
908 | case 3:
909 | return CLASS_HIDE_SM_DOWN;
910 |
911 | case 4:
912 | return CLASS_HIDE_MD_DOWN;
913 |
914 | default:
915 | return '';
916 | }
917 | }
918 | /**
919 | * Get the max ratio of a group of pointers.
920 | * @param {string} pointers - The target pointers.
921 | * @returns {number} The result ratio.
922 | */
923 |
924 | function getMaxZoomRatio(pointers) {
925 | var pointers2 = _objectSpread2({}, pointers);
926 |
927 | var ratios = [];
928 | forEach(pointers, function (pointer, pointerId) {
929 | delete pointers2[pointerId];
930 | forEach(pointers2, function (pointer2) {
931 | var x1 = Math.abs(pointer.startX - pointer2.startX);
932 | var y1 = Math.abs(pointer.startY - pointer2.startY);
933 | var x2 = Math.abs(pointer.endX - pointer2.endX);
934 | var y2 = Math.abs(pointer.endY - pointer2.endY);
935 | var z1 = Math.sqrt(x1 * x1 + y1 * y1);
936 | var z2 = Math.sqrt(x2 * x2 + y2 * y2);
937 | var ratio = (z2 - z1) / z1;
938 | ratios.push(ratio);
939 | });
940 | });
941 | ratios.sort(function (a, b) {
942 | return Math.abs(a) < Math.abs(b);
943 | });
944 | return ratios[0];
945 | }
946 | /**
947 | * Get a pointer from an event object.
948 | * @param {Object} event - The target event object.
949 | * @param {boolean} endOnly - Indicates if only returns the end point coordinate or not.
950 | * @returns {Object} The result pointer contains start and/or end point coordinates.
951 | */
952 |
953 | function getPointer(_ref2, endOnly) {
954 | var pageX = _ref2.pageX,
955 | pageY = _ref2.pageY;
956 | var end = {
957 | endX: pageX,
958 | endY: pageY
959 | };
960 | return endOnly ? end : _objectSpread2({
961 | timeStamp: Date.now(),
962 | startX: pageX,
963 | startY: pageY
964 | }, end);
965 | }
966 | /**
967 | * Get the center point coordinate of a group of pointers.
968 | * @param {Object} pointers - The target pointers.
969 | * @returns {Object} The center point coordinate.
970 | */
971 |
972 | function getPointersCenter(pointers) {
973 | var pageX = 0;
974 | var pageY = 0;
975 | var count = 0;
976 | forEach(pointers, function (_ref3) {
977 | var startX = _ref3.startX,
978 | startY = _ref3.startY;
979 | pageX += startX;
980 | pageY += startY;
981 | count += 1;
982 | });
983 | pageX /= count;
984 | pageY /= count;
985 | return {
986 | pageX: pageX,
987 | pageY: pageY
988 | };
989 | }
990 |
991 | var render = {
992 | render: function render() {
993 | this.initContainer();
994 | this.initViewer();
995 | this.initList();
996 | this.renderViewer();
997 | },
998 | initContainer: function initContainer() {
999 | this.containerData = {
1000 | width: window.innerWidth,
1001 | height: window.innerHeight
1002 | };
1003 | },
1004 | initViewer: function initViewer() {
1005 | var options = this.options,
1006 | parent = this.parent;
1007 | var viewerData;
1008 |
1009 | if (options.inline) {
1010 | viewerData = {
1011 | width: Math.max(parent.offsetWidth, options.minWidth),
1012 | height: Math.max(parent.offsetHeight, options.minHeight)
1013 | };
1014 | this.parentData = viewerData;
1015 | }
1016 |
1017 | if (this.fulled || !viewerData) {
1018 | viewerData = this.containerData;
1019 | }
1020 |
1021 | this.viewerData = assign({}, viewerData);
1022 | },
1023 | renderViewer: function renderViewer() {
1024 | if (this.options.inline && !this.fulled) {
1025 | setStyle(this.viewer, this.viewerData);
1026 | }
1027 | },
1028 | initList: function initList() {
1029 | var _this = this;
1030 |
1031 | var element = this.element,
1032 | options = this.options,
1033 | list = this.list;
1034 | var items = []; // initList may be called in this.update, so should keep idempotent
1035 |
1036 | list.innerHTML = '';
1037 | forEach(this.images, function (image, index) {
1038 | var src = image.src;
1039 | var alt = image.alt || getImageNameFromURL(src);
1040 | var url = options.url;
1041 |
1042 | if (isString(url)) {
1043 | url = image.getAttribute(url);
1044 | } else if (isFunction(url)) {
1045 | url = url.call(_this, image);
1046 | }
1047 |
1048 | if (src || url) {
1049 | var item = document.createElement('li');
1050 | var img = document.createElement('img');
1051 | img.src = src || url;
1052 | img.alt = alt;
1053 | img.setAttribute('data-index', index);
1054 | img.setAttribute('data-original-url', url || src);
1055 | img.setAttribute('data-viewer-action', 'view');
1056 | img.setAttribute('role', 'button');
1057 | item.appendChild(img);
1058 | list.appendChild(item);
1059 | items.push(item);
1060 | }
1061 | });
1062 | this.items = items;
1063 | forEach(items, function (item) {
1064 | var image = item.firstElementChild;
1065 | setData(image, 'filled', true);
1066 |
1067 | if (options.loading) {
1068 | addClass(item, CLASS_LOADING);
1069 | }
1070 |
1071 | addListener(image, EVENT_LOAD, function (event) {
1072 | if (options.loading) {
1073 | removeClass(item, CLASS_LOADING);
1074 | }
1075 |
1076 | _this.loadImage(event);
1077 | }, {
1078 | once: true
1079 | });
1080 | });
1081 |
1082 | if (options.transition) {
1083 | addListener(element, EVENT_VIEWED, function () {
1084 | addClass(list, CLASS_TRANSITION);
1085 | }, {
1086 | once: true
1087 | });
1088 | }
1089 | },
1090 | renderList: function renderList(index) {
1091 | var i = index || this.index;
1092 | var width = this.items[i].offsetWidth || 30;
1093 | var outerWidth = width + 1; // 1 pixel of `margin-left` width
1094 | // Place the active item in the center of the screen
1095 |
1096 | setStyle(this.list, assign({
1097 | width: outerWidth * this.length
1098 | }, getTransforms({
1099 | translateX: (this.viewerData.width - width) / 2 - outerWidth * i
1100 | })));
1101 | },
1102 | resetList: function resetList() {
1103 | var list = this.list;
1104 | list.innerHTML = '';
1105 | removeClass(list, CLASS_TRANSITION);
1106 | setStyle(list, getTransforms({
1107 | translateX: 0
1108 | }));
1109 | },
1110 | initImage: function initImage(done) {
1111 | var _this2 = this;
1112 |
1113 | var options = this.options,
1114 | image = this.image,
1115 | viewerData = this.viewerData;
1116 | var footerHeight = this.footer.offsetHeight;
1117 | var viewerWidth = viewerData.width;
1118 | var viewerHeight = Math.max(viewerData.height - footerHeight, footerHeight);
1119 | var oldImageData = this.imageData || {};
1120 | var sizingImage;
1121 | this.imageInitializing = {
1122 | abort: function abort() {
1123 | sizingImage.onload = null;
1124 | }
1125 | };
1126 | sizingImage = getImageNaturalSizes(image, function (naturalWidth, naturalHeight) {
1127 | var aspectRatio = naturalWidth / naturalHeight;
1128 | var width = viewerWidth;
1129 | var height = viewerHeight;
1130 | _this2.imageInitializing = false;
1131 |
1132 | if (viewerHeight * aspectRatio > viewerWidth) {
1133 | height = viewerWidth / aspectRatio;
1134 | } else {
1135 | width = viewerHeight * aspectRatio;
1136 | }
1137 |
1138 | width = Math.min(width * 0.9, naturalWidth);
1139 | height = Math.min(height * 0.9, naturalHeight);
1140 | var imageData = {
1141 | naturalWidth: naturalWidth,
1142 | naturalHeight: naturalHeight,
1143 | aspectRatio: aspectRatio,
1144 | ratio: width / naturalWidth,
1145 | width: width,
1146 | height: height,
1147 | left: (viewerWidth - width) / 2,
1148 | top: (viewerHeight - height) / 2
1149 | };
1150 | var initialImageData = assign({}, imageData);
1151 |
1152 | if (options.rotatable) {
1153 | imageData.rotate = oldImageData.rotate || 0;
1154 | initialImageData.rotate = 0;
1155 | }
1156 |
1157 | if (options.scalable) {
1158 | imageData.scaleX = oldImageData.scaleX || 1;
1159 | imageData.scaleY = oldImageData.scaleY || 1;
1160 | initialImageData.scaleX = 1;
1161 | initialImageData.scaleY = 1;
1162 | }
1163 |
1164 | _this2.imageData = imageData;
1165 | _this2.initialImageData = initialImageData;
1166 |
1167 | if (done) {
1168 | done();
1169 | }
1170 | });
1171 | },
1172 | renderImage: function renderImage(done) {
1173 | var _this3 = this;
1174 |
1175 | var image = this.image,
1176 | imageData = this.imageData;
1177 | setStyle(image, assign({
1178 | width: imageData.width,
1179 | height: imageData.height,
1180 | // XXX: Not to use translateX/Y to avoid image shaking when zooming
1181 | marginLeft: imageData.left,
1182 | marginTop: imageData.top
1183 | }, getTransforms(imageData)));
1184 |
1185 | if (done) {
1186 | if ((this.viewing || this.zooming) && this.options.transition) {
1187 | var onTransitionEnd = function onTransitionEnd() {
1188 | _this3.imageRendering = false;
1189 | done();
1190 | };
1191 |
1192 | this.imageRendering = {
1193 | abort: function abort() {
1194 | removeListener(image, EVENT_TRANSITION_END, onTransitionEnd);
1195 | }
1196 | };
1197 | addListener(image, EVENT_TRANSITION_END, onTransitionEnd, {
1198 | once: true
1199 | });
1200 | } else {
1201 | done();
1202 | }
1203 | }
1204 | },
1205 | resetImage: function resetImage() {
1206 | // this.image only defined after viewed
1207 | if (this.viewing || this.viewed) {
1208 | var image = this.image;
1209 |
1210 | if (this.viewing) {
1211 | this.viewing.abort();
1212 | }
1213 |
1214 | image.parentNode.removeChild(image);
1215 | this.image = null;
1216 | }
1217 | }
1218 | };
1219 |
1220 | var events = {
1221 | bind: function bind() {
1222 | var options = this.options,
1223 | viewer = this.viewer,
1224 | canvas = this.canvas;
1225 | var document = this.element.ownerDocument;
1226 | addListener(viewer, EVENT_CLICK, this.onClick = this.click.bind(this));
1227 | addListener(viewer, EVENT_DRAG_START, this.onDragStart = this.dragstart.bind(this));
1228 | addListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown = this.pointerdown.bind(this));
1229 | addListener(document, EVENT_POINTER_MOVE, this.onPointerMove = this.pointermove.bind(this));
1230 | addListener(document, EVENT_POINTER_UP, this.onPointerUp = this.pointerup.bind(this));
1231 | addListener(document, EVENT_KEY_DOWN, this.onKeyDown = this.keydown.bind(this));
1232 | addListener(window, EVENT_RESIZE, this.onResize = this.resize.bind(this));
1233 |
1234 | if (options.zoomable && options.zoomOnWheel) {
1235 | addListener(viewer, EVENT_WHEEL, this.onWheel = this.wheel.bind(this), {
1236 | passive: false,
1237 | capture: true
1238 | });
1239 | }
1240 |
1241 | if (options.toggleOnDblclick) {
1242 | addListener(canvas, EVENT_DBLCLICK, this.onDblclick = this.dblclick.bind(this));
1243 | }
1244 | },
1245 | unbind: function unbind() {
1246 | var options = this.options,
1247 | viewer = this.viewer,
1248 | canvas = this.canvas;
1249 | var document = this.element.ownerDocument;
1250 | removeListener(viewer, EVENT_CLICK, this.onClick);
1251 | removeListener(viewer, EVENT_DRAG_START, this.onDragStart);
1252 | removeListener(canvas, EVENT_POINTER_DOWN, this.onPointerDown);
1253 | removeListener(document, EVENT_POINTER_MOVE, this.onPointerMove);
1254 | removeListener(document, EVENT_POINTER_UP, this.onPointerUp);
1255 | removeListener(document, EVENT_KEY_DOWN, this.onKeyDown);
1256 | removeListener(window, EVENT_RESIZE, this.onResize);
1257 |
1258 | if (options.zoomable && options.zoomOnWheel) {
1259 | removeListener(viewer, EVENT_WHEEL, this.onWheel, {
1260 | passive: false,
1261 | capture: true
1262 | });
1263 | }
1264 |
1265 | if (options.toggleOnDblclick) {
1266 | removeListener(canvas, EVENT_DBLCLICK, this.onDblclick);
1267 | }
1268 | }
1269 | };
1270 |
1271 | var handlers = {
1272 | click: function click(event) {
1273 | var target = event.target;
1274 | var options = this.options,
1275 | imageData = this.imageData;
1276 | var action = getData(target, DATA_ACTION); // Cancel the emulated click when the native click event was triggered.
1277 |
1278 | if (IS_TOUCH_DEVICE && event.isTrusted && target === this.canvas) {
1279 | clearTimeout(this.clickCanvasTimeout);
1280 | }
1281 |
1282 | switch (action) {
1283 | case 'mix':
1284 | if (this.played) {
1285 | this.stop();
1286 | } else if (options.inline) {
1287 | if (this.fulled) {
1288 | this.exit();
1289 | } else {
1290 | this.full();
1291 | }
1292 | } else {
1293 | this.hide();
1294 | }
1295 |
1296 | break;
1297 |
1298 | case 'hide':
1299 | this.hide();
1300 | break;
1301 |
1302 | case 'view':
1303 | this.view(getData(target, 'index'));
1304 | break;
1305 |
1306 | case 'zoom-in':
1307 | this.zoom(0.1, true);
1308 | break;
1309 |
1310 | case 'zoom-out':
1311 | this.zoom(-0.1, true);
1312 | break;
1313 |
1314 | case 'one-to-one':
1315 | this.toggle();
1316 | break;
1317 |
1318 | case 'reset':
1319 | this.reset();
1320 | break;
1321 |
1322 | case 'prev':
1323 | this.prev(options.loop);
1324 | break;
1325 |
1326 | case 'play':
1327 | this.play(options.fullscreen);
1328 | break;
1329 |
1330 | case 'next':
1331 | this.next(options.loop);
1332 | break;
1333 |
1334 | case 'rotate-left':
1335 | this.rotate(-90);
1336 | break;
1337 |
1338 | case 'rotate-right':
1339 | this.rotate(90);
1340 | break;
1341 |
1342 | case 'flip-horizontal':
1343 | this.scaleX(-imageData.scaleX || -1);
1344 | break;
1345 |
1346 | case 'flip-vertical':
1347 | this.scaleY(-imageData.scaleY || -1);
1348 | break;
1349 |
1350 | default:
1351 | if (this.played) {
1352 | this.stop();
1353 | }
1354 |
1355 | }
1356 | },
1357 | dblclick: function dblclick(event) {
1358 | event.preventDefault();
1359 |
1360 | if (this.viewed && event.target === this.image) {
1361 | // Cancel the emulated double click when the native dblclick event was triggered.
1362 | if (IS_TOUCH_DEVICE && event.isTrusted) {
1363 | clearTimeout(this.doubleClickImageTimeout);
1364 | }
1365 |
1366 | this.toggle();
1367 | }
1368 | },
1369 | load: function load() {
1370 | var _this = this;
1371 |
1372 | if (this.timeout) {
1373 | clearTimeout(this.timeout);
1374 | this.timeout = false;
1375 | }
1376 |
1377 | var element = this.element,
1378 | options = this.options,
1379 | image = this.image,
1380 | index = this.index,
1381 | viewerData = this.viewerData;
1382 | removeClass(image, CLASS_INVISIBLE);
1383 |
1384 | if (options.loading) {
1385 | removeClass(this.canvas, CLASS_LOADING);
1386 | }
1387 |
1388 | image.style.cssText = 'height:0;' + "margin-left:".concat(viewerData.width / 2, "px;") + "margin-top:".concat(viewerData.height / 2, "px;") + 'max-width:none!important;' + 'position:absolute;' + 'width:0;';
1389 | this.initImage(function () {
1390 | toggleClass(image, CLASS_MOVE, options.movable);
1391 | toggleClass(image, CLASS_TRANSITION, options.transition);
1392 |
1393 | _this.renderImage(function () {
1394 | _this.viewed = true;
1395 | _this.viewing = false;
1396 |
1397 | if (isFunction(options.viewed)) {
1398 | addListener(element, EVENT_VIEWED, options.viewed, {
1399 | once: true
1400 | });
1401 | }
1402 |
1403 | dispatchEvent(element, EVENT_VIEWED, {
1404 | originalImage: _this.images[index],
1405 | index: index,
1406 | image: image
1407 | });
1408 | });
1409 | });
1410 | },
1411 | loadImage: function loadImage(event) {
1412 | var image = event.target;
1413 | var parent = image.parentNode;
1414 | var parentWidth = parent.offsetWidth || 30;
1415 | var parentHeight = parent.offsetHeight || 50;
1416 | var filled = !!getData(image, 'filled');
1417 | getImageNaturalSizes(image, function (naturalWidth, naturalHeight) {
1418 | var aspectRatio = naturalWidth / naturalHeight;
1419 | var width = parentWidth;
1420 | var height = parentHeight;
1421 |
1422 | if (parentHeight * aspectRatio > parentWidth) {
1423 | if (filled) {
1424 | width = parentHeight * aspectRatio;
1425 | } else {
1426 | height = parentWidth / aspectRatio;
1427 | }
1428 | } else if (filled) {
1429 | height = parentWidth / aspectRatio;
1430 | } else {
1431 | width = parentHeight * aspectRatio;
1432 | }
1433 |
1434 | setStyle(image, assign({
1435 | width: width,
1436 | height: height
1437 | }, getTransforms({
1438 | translateX: (parentWidth - width) / 2,
1439 | translateY: (parentHeight - height) / 2
1440 | })));
1441 | });
1442 | },
1443 | keydown: function keydown(event) {
1444 | var options = this.options;
1445 |
1446 | if (!this.fulled || !options.keyboard) {
1447 | return;
1448 | }
1449 |
1450 | switch (event.keyCode || event.which || event.charCode) {
1451 | // Escape
1452 | case 27:
1453 | if (this.played) {
1454 | this.stop();
1455 | } else if (options.inline) {
1456 | if (this.fulled) {
1457 | this.exit();
1458 | }
1459 | } else {
1460 | this.hide();
1461 | }
1462 |
1463 | break;
1464 | // Space
1465 |
1466 | case 32:
1467 | if (this.played) {
1468 | this.stop();
1469 | }
1470 |
1471 | break;
1472 | // ArrowLeft
1473 |
1474 | case 37:
1475 | this.prev(options.loop);
1476 | break;
1477 | // ArrowUp
1478 |
1479 | case 38:
1480 | // Prevent scroll on Firefox
1481 | event.preventDefault(); // Zoom in
1482 |
1483 | this.zoom(options.zoomRatio, true);
1484 | break;
1485 | // ArrowRight
1486 |
1487 | case 39:
1488 | this.next(options.loop);
1489 | break;
1490 | // ArrowDown
1491 |
1492 | case 40:
1493 | // Prevent scroll on Firefox
1494 | event.preventDefault(); // Zoom out
1495 |
1496 | this.zoom(-options.zoomRatio, true);
1497 | break;
1498 | // Ctrl + 0
1499 |
1500 | case 48: // Fall through
1501 | // Ctrl + 1
1502 | // eslint-disable-next-line no-fallthrough
1503 |
1504 | case 49:
1505 | if (event.ctrlKey) {
1506 | event.preventDefault();
1507 | this.toggle();
1508 | }
1509 |
1510 | break;
1511 | }
1512 | },
1513 | dragstart: function dragstart(event) {
1514 | if (event.target.tagName.toLowerCase() === 'img') {
1515 | event.preventDefault();
1516 | }
1517 | },
1518 | pointerdown: function pointerdown(event) {
1519 | var options = this.options,
1520 | pointers = this.pointers;
1521 | var buttons = event.buttons,
1522 | button = event.button;
1523 |
1524 | if (!this.viewed || this.showing || this.viewing || this.hiding // Handle mouse event and pointer event and ignore touch event
1525 | || (event.type === 'mousedown' || event.type === 'pointerdown' && event.pointerType === 'mouse') && ( // No primary button (Usually the left button)
1526 | isNumber(buttons) && buttons !== 1 || isNumber(button) && button !== 0 // Open context menu
1527 | || event.ctrlKey)) {
1528 | return;
1529 | } // Prevent default behaviours as page zooming in touch devices.
1530 |
1531 |
1532 | event.preventDefault();
1533 |
1534 | if (event.changedTouches) {
1535 | forEach(event.changedTouches, function (touch) {
1536 | pointers[touch.identifier] = getPointer(touch);
1537 | });
1538 | } else {
1539 | pointers[event.pointerId || 0] = getPointer(event);
1540 | }
1541 |
1542 | var action = options.movable ? ACTION_MOVE : false;
1543 |
1544 | if (options.zoomOnTouch && options.zoomable && Object.keys(pointers).length > 1) {
1545 | action = ACTION_ZOOM;
1546 | } else if (options.slideOnTouch && (event.pointerType === 'touch' || event.type === 'touchstart') && this.isSwitchable()) {
1547 | action = ACTION_SWITCH;
1548 | }
1549 |
1550 | if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
1551 | removeClass(this.image, CLASS_TRANSITION);
1552 | }
1553 |
1554 | this.action = action;
1555 | },
1556 | pointermove: function pointermove(event) {
1557 | var pointers = this.pointers,
1558 | action = this.action;
1559 |
1560 | if (!this.viewed || !action) {
1561 | return;
1562 | }
1563 |
1564 | event.preventDefault();
1565 |
1566 | if (event.changedTouches) {
1567 | forEach(event.changedTouches, function (touch) {
1568 | assign(pointers[touch.identifier] || {}, getPointer(touch, true));
1569 | });
1570 | } else {
1571 | assign(pointers[event.pointerId || 0] || {}, getPointer(event, true));
1572 | }
1573 |
1574 | this.change(event);
1575 | },
1576 | pointerup: function pointerup(event) {
1577 | var _this2 = this;
1578 |
1579 | var options = this.options,
1580 | action = this.action,
1581 | pointers = this.pointers;
1582 | var pointer;
1583 |
1584 | if (event.changedTouches) {
1585 | forEach(event.changedTouches, function (touch) {
1586 | pointer = pointers[touch.identifier];
1587 | delete pointers[touch.identifier];
1588 | });
1589 | } else {
1590 | pointer = pointers[event.pointerId || 0];
1591 | delete pointers[event.pointerId || 0];
1592 | }
1593 |
1594 | if (!action) {
1595 | return;
1596 | }
1597 |
1598 | event.preventDefault();
1599 |
1600 | if (options.transition && (action === ACTION_MOVE || action === ACTION_ZOOM)) {
1601 | addClass(this.image, CLASS_TRANSITION);
1602 | }
1603 |
1604 | this.action = false; // Emulate click and double click in touch devices to support backdrop and image zooming (#210).
1605 |
1606 | if (IS_TOUCH_DEVICE && action !== ACTION_ZOOM && pointer && Date.now() - pointer.timeStamp < 500) {
1607 | clearTimeout(this.clickCanvasTimeout);
1608 | clearTimeout(this.doubleClickImageTimeout);
1609 |
1610 | if (options.toggleOnDblclick && this.viewed && event.target === this.image) {
1611 | if (this.imageClicked) {
1612 | this.imageClicked = false; // This timeout will be cleared later when a native dblclick event is triggering
1613 |
1614 | this.doubleClickImageTimeout = setTimeout(function () {
1615 | dispatchEvent(_this2.image, EVENT_DBLCLICK);
1616 | }, 50);
1617 | } else {
1618 | this.imageClicked = true; // The default timing of a double click in Windows is 500 ms
1619 |
1620 | this.doubleClickImageTimeout = setTimeout(function () {
1621 | _this2.imageClicked = false;
1622 | }, 500);
1623 | }
1624 | } else {
1625 | this.imageClicked = false;
1626 |
1627 | if (options.backdrop && options.backdrop !== 'static' && event.target === this.canvas) {
1628 | // This timeout will be cleared later when a native click event is triggering
1629 | this.clickCanvasTimeout = setTimeout(function () {
1630 | dispatchEvent(_this2.canvas, EVENT_CLICK);
1631 | }, 50);
1632 | }
1633 | }
1634 | }
1635 | },
1636 | resize: function resize() {
1637 | var _this3 = this;
1638 |
1639 | if (!this.isShown || this.hiding) {
1640 | return;
1641 | }
1642 |
1643 | this.initContainer();
1644 | this.initViewer();
1645 | this.renderViewer();
1646 | this.renderList();
1647 |
1648 | if (this.viewed) {
1649 | this.initImage(function () {
1650 | _this3.renderImage();
1651 | });
1652 | }
1653 |
1654 | if (this.played) {
1655 | if (this.options.fullscreen && this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
1656 | this.stop();
1657 | return;
1658 | }
1659 |
1660 | forEach(this.player.getElementsByTagName('img'), function (image) {
1661 | addListener(image, EVENT_LOAD, _this3.loadImage.bind(_this3), {
1662 | once: true
1663 | });
1664 | dispatchEvent(image, EVENT_LOAD);
1665 | });
1666 | }
1667 | },
1668 | wheel: function wheel(event) {
1669 | var _this4 = this;
1670 |
1671 | if (!this.viewed) {
1672 | return;
1673 | }
1674 |
1675 | event.preventDefault(); // Limit wheel speed to prevent zoom too fast
1676 |
1677 | if (this.wheeling) {
1678 | return;
1679 | }
1680 |
1681 | this.wheeling = true;
1682 | setTimeout(function () {
1683 | _this4.wheeling = false;
1684 | }, 50);
1685 | var ratio = Number(this.options.zoomRatio) || 0.1;
1686 | var delta = 1;
1687 |
1688 | if (event.deltaY) {
1689 | delta = event.deltaY > 0 ? 1 : -1;
1690 | } else if (event.wheelDelta) {
1691 | delta = -event.wheelDelta / 120;
1692 | } else if (event.detail) {
1693 | delta = event.detail > 0 ? 1 : -1;
1694 | }
1695 |
1696 | this.zoom(-delta * ratio, true, event);
1697 | }
1698 | };
1699 |
1700 | var methods = {
1701 | /** Show the viewer (only available in modal mode)
1702 | * @param {boolean} [immediate=false] - Indicates if show the viewer immediately or not.
1703 | * @returns {Viewer} this
1704 | */
1705 | show: function show() {
1706 | var immediate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
1707 | var element = this.element,
1708 | options = this.options;
1709 |
1710 | if (options.inline || this.showing || this.isShown || this.showing) {
1711 | return this;
1712 | }
1713 |
1714 | if (!this.ready) {
1715 | this.build();
1716 |
1717 | if (this.ready) {
1718 | this.show(immediate);
1719 | }
1720 |
1721 | return this;
1722 | }
1723 |
1724 | if (isFunction(options.show)) {
1725 | addListener(element, EVENT_SHOW, options.show, {
1726 | once: true
1727 | });
1728 | }
1729 |
1730 | if (dispatchEvent(element, EVENT_SHOW) === false || !this.ready) {
1731 | return this;
1732 | }
1733 |
1734 | if (this.hiding) {
1735 | this.transitioning.abort();
1736 | }
1737 |
1738 | this.showing = true;
1739 | this.open();
1740 | var viewer = this.viewer;
1741 | removeClass(viewer, CLASS_HIDE);
1742 |
1743 | if (options.transition && !immediate) {
1744 | var shown = this.shown.bind(this);
1745 | this.transitioning = {
1746 | abort: function abort() {
1747 | removeListener(viewer, EVENT_TRANSITION_END, shown);
1748 | removeClass(viewer, CLASS_IN);
1749 | }
1750 | };
1751 | addClass(viewer, CLASS_TRANSITION); // Force reflow to enable CSS3 transition
1752 |
1753 | viewer.initialOffsetWidth = viewer.offsetWidth;
1754 | addListener(viewer, EVENT_TRANSITION_END, shown, {
1755 | once: true
1756 | });
1757 | addClass(viewer, CLASS_IN);
1758 | } else {
1759 | addClass(viewer, CLASS_IN);
1760 | this.shown();
1761 | }
1762 |
1763 | return this;
1764 | },
1765 |
1766 | /**
1767 | * Hide the viewer (only available in modal mode)
1768 | * @param {boolean} [immediate=false] - Indicates if hide the viewer immediately or not.
1769 | * @returns {Viewer} this
1770 | */
1771 | hide: function hide() {
1772 | var immediate = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
1773 | var element = this.element,
1774 | options = this.options;
1775 |
1776 | if (options.inline || this.hiding || !(this.isShown || this.showing)) {
1777 | return this;
1778 | }
1779 |
1780 | if (isFunction(options.hide)) {
1781 | addListener(element, EVENT_HIDE, options.hide, {
1782 | once: true
1783 | });
1784 | }
1785 |
1786 | if (dispatchEvent(element, EVENT_HIDE) === false) {
1787 | return this;
1788 | }
1789 |
1790 | if (this.showing) {
1791 | this.transitioning.abort();
1792 | }
1793 |
1794 | this.hiding = true;
1795 |
1796 | if (this.played) {
1797 | this.stop();
1798 | } else if (this.viewing) {
1799 | this.viewing.abort();
1800 | }
1801 |
1802 | var viewer = this.viewer;
1803 |
1804 | if (options.transition && !immediate) {
1805 | var hidden = this.hidden.bind(this);
1806 |
1807 | var hide = function hide() {
1808 | // XXX: It seems the `event.stopPropagation()` method does not work here
1809 | setTimeout(function () {
1810 | addListener(viewer, EVENT_TRANSITION_END, hidden, {
1811 | once: true
1812 | });
1813 | removeClass(viewer, CLASS_IN);
1814 | }, 0);
1815 | };
1816 |
1817 | this.transitioning = {
1818 | abort: function abort() {
1819 | if (this.viewed) {
1820 | removeListener(this.image, EVENT_TRANSITION_END, hide);
1821 | } else {
1822 | removeListener(viewer, EVENT_TRANSITION_END, hidden);
1823 | }
1824 | }
1825 | }; // Note that the `CLASS_TRANSITION` class will be removed on pointer down (#255)
1826 |
1827 | if (this.viewed && hasClass(this.image, CLASS_TRANSITION)) {
1828 | addListener(this.image, EVENT_TRANSITION_END, hide, {
1829 | once: true
1830 | });
1831 | this.zoomTo(0, false, false, true);
1832 | } else {
1833 | hide();
1834 | }
1835 | } else {
1836 | removeClass(viewer, CLASS_IN);
1837 | this.hidden();
1838 | }
1839 |
1840 | return this;
1841 | },
1842 |
1843 | /**
1844 | * View one of the images with image's index
1845 | * @param {number} index - The index of the image to view.
1846 | * @returns {Viewer} this
1847 | */
1848 | view: function view() {
1849 | var _this = this;
1850 |
1851 | var index = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.options.initialViewIndex;
1852 | index = Number(index) || 0;
1853 |
1854 | if (this.hiding || this.played || index < 0 || index >= this.length || this.viewed && index === this.index) {
1855 | return this;
1856 | }
1857 |
1858 | if (!this.isShown) {
1859 | this.index = index;
1860 | return this.show();
1861 | }
1862 |
1863 | if (this.viewing) {
1864 | this.viewing.abort();
1865 | }
1866 |
1867 | var element = this.element,
1868 | options = this.options,
1869 | title = this.title,
1870 | canvas = this.canvas;
1871 | var item = this.items[index];
1872 | var img = item.querySelector('img');
1873 | var url = getData(img, 'originalUrl');
1874 | var alt = img.getAttribute('alt');
1875 | var image = document.createElement('img');
1876 | image.src = url;
1877 | image.alt = alt;
1878 |
1879 | if (isFunction(options.view)) {
1880 | addListener(element, EVENT_VIEW, options.view, {
1881 | once: true
1882 | });
1883 | }
1884 |
1885 | if (dispatchEvent(element, EVENT_VIEW, {
1886 | originalImage: this.images[index],
1887 | index: index,
1888 | image: image
1889 | }) === false || !this.isShown || this.hiding || this.played) {
1890 | return this;
1891 | }
1892 |
1893 | this.image = image;
1894 | removeClass(this.items[this.index], CLASS_ACTIVE);
1895 | addClass(item, CLASS_ACTIVE);
1896 | this.viewed = false;
1897 | this.index = index;
1898 | this.imageData = {};
1899 | addClass(image, CLASS_INVISIBLE);
1900 |
1901 | if (options.loading) {
1902 | addClass(canvas, CLASS_LOADING);
1903 | }
1904 |
1905 | canvas.innerHTML = '';
1906 | canvas.appendChild(image); // Center current item
1907 |
1908 | this.renderList(); // Clear title
1909 |
1910 | title.innerHTML = ''; // Generate title after viewed
1911 |
1912 | var onViewed = function onViewed() {
1913 | var imageData = _this.imageData;
1914 | var render = Array.isArray(options.title) ? options.title[1] : options.title;
1915 | title.innerHTML = escapeHTMLEntities(isFunction(render) ? render.call(_this, image, imageData) : "".concat(alt, " (").concat(imageData.naturalWidth, " \xD7 ").concat(imageData.naturalHeight, ")"));
1916 | };
1917 |
1918 | var onLoad;
1919 | addListener(element, EVENT_VIEWED, onViewed, {
1920 | once: true
1921 | });
1922 | this.viewing = {
1923 | abort: function abort() {
1924 | removeListener(element, EVENT_VIEWED, onViewed);
1925 |
1926 | if (image.complete) {
1927 | if (this.imageRendering) {
1928 | this.imageRendering.abort();
1929 | } else if (this.imageInitializing) {
1930 | this.imageInitializing.abort();
1931 | }
1932 | } else {
1933 | // Cancel download to save bandwidth.
1934 | image.src = '';
1935 | removeListener(image, EVENT_LOAD, onLoad);
1936 |
1937 | if (this.timeout) {
1938 | clearTimeout(this.timeout);
1939 | }
1940 | }
1941 | }
1942 | };
1943 |
1944 | if (image.complete) {
1945 | this.load();
1946 | } else {
1947 | addListener(image, EVENT_LOAD, onLoad = this.load.bind(this), {
1948 | once: true
1949 | });
1950 |
1951 | if (this.timeout) {
1952 | clearTimeout(this.timeout);
1953 | } // Make the image visible if it fails to load within 1s
1954 |
1955 |
1956 | this.timeout = setTimeout(function () {
1957 | removeClass(image, CLASS_INVISIBLE);
1958 | _this.timeout = false;
1959 | }, 1000);
1960 | }
1961 |
1962 | return this;
1963 | },
1964 |
1965 | /**
1966 | * View the previous image
1967 | * @param {boolean} [loop=false] - Indicate if view the last one
1968 | * when it is the first one at present.
1969 | * @returns {Viewer} this
1970 | */
1971 | prev: function prev() {
1972 | var loop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
1973 | var index = this.index - 1;
1974 |
1975 | if (index < 0) {
1976 | index = loop ? this.length - 1 : 0;
1977 | }
1978 |
1979 | this.view(index);
1980 | return this;
1981 | },
1982 |
1983 | /**
1984 | * View the next image
1985 | * @param {boolean} [loop=false] - Indicate if view the first one
1986 | * when it is the last one at present.
1987 | * @returns {Viewer} this
1988 | */
1989 | next: function next() {
1990 | var loop = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
1991 | var maxIndex = this.length - 1;
1992 | var index = this.index + 1;
1993 |
1994 | if (index > maxIndex) {
1995 | index = loop ? 0 : maxIndex;
1996 | }
1997 |
1998 | this.view(index);
1999 | return this;
2000 | },
2001 |
2002 | /**
2003 | * Move the image with relative offsets.
2004 | * @param {number} offsetX - The relative offset distance on the x-axis.
2005 | * @param {number} offsetY - The relative offset distance on the y-axis.
2006 | * @returns {Viewer} this
2007 | */
2008 | move: function move(offsetX, offsetY) {
2009 | var imageData = this.imageData;
2010 | this.moveTo(isUndefined(offsetX) ? offsetX : imageData.left + Number(offsetX), isUndefined(offsetY) ? offsetY : imageData.top + Number(offsetY));
2011 | return this;
2012 | },
2013 |
2014 | /**
2015 | * Move the image to an absolute point.
2016 | * @param {number} x - The x-axis coordinate.
2017 | * @param {number} [y=x] - The y-axis coordinate.
2018 | * @returns {Viewer} this
2019 | */
2020 | moveTo: function moveTo(x) {
2021 | var y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
2022 | var imageData = this.imageData;
2023 | x = Number(x);
2024 | y = Number(y);
2025 |
2026 | if (this.viewed && !this.played && this.options.movable) {
2027 | var changed = false;
2028 |
2029 | if (isNumber(x)) {
2030 | imageData.left = x;
2031 | changed = true;
2032 | }
2033 |
2034 | if (isNumber(y)) {
2035 | imageData.top = y;
2036 | changed = true;
2037 | }
2038 |
2039 | if (changed) {
2040 | this.renderImage();
2041 | }
2042 | }
2043 |
2044 | return this;
2045 | },
2046 |
2047 | /**
2048 | * Zoom the image with a relative ratio.
2049 | * @param {number} ratio - The target ratio.
2050 | * @param {boolean} [hasTooltip=false] - Indicates if it has a tooltip or not.
2051 | * @param {Event} [_originalEvent=null] - The original event if any.
2052 | * @returns {Viewer} this
2053 | */
2054 | zoom: function zoom(ratio) {
2055 | var hasTooltip = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2056 |
2057 | var _originalEvent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
2058 |
2059 | var imageData = this.imageData;
2060 | ratio = Number(ratio);
2061 |
2062 | if (ratio < 0) {
2063 | ratio = 1 / (1 - ratio);
2064 | } else {
2065 | ratio = 1 + ratio;
2066 | }
2067 |
2068 | this.zoomTo(imageData.width * ratio / imageData.naturalWidth, hasTooltip, _originalEvent);
2069 | return this;
2070 | },
2071 |
2072 | /**
2073 | * Zoom the image to an absolute ratio.
2074 | * @param {number} ratio - The target ratio.
2075 | * @param {boolean} [hasTooltip=false] - Indicates if it has a tooltip or not.
2076 | * @param {Event} [_originalEvent=null] - The original event if any.
2077 | * @param {Event} [_zoomable=false] - Indicates if the current zoom is available or not.
2078 | * @returns {Viewer} this
2079 | */
2080 | zoomTo: function zoomTo(ratio) {
2081 | var _this2 = this;
2082 |
2083 | var hasTooltip = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
2084 |
2085 | var _originalEvent = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
2086 |
2087 | var _zoomable = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
2088 |
2089 | var element = this.element,
2090 | options = this.options,
2091 | pointers = this.pointers,
2092 | imageData = this.imageData;
2093 | var width = imageData.width,
2094 | height = imageData.height,
2095 | left = imageData.left,
2096 | top = imageData.top,
2097 | naturalWidth = imageData.naturalWidth,
2098 | naturalHeight = imageData.naturalHeight;
2099 | ratio = Math.max(0, ratio);
2100 |
2101 | if (isNumber(ratio) && this.viewed && !this.played && (_zoomable || options.zoomable)) {
2102 | if (!_zoomable) {
2103 | var minZoomRatio = Math.max(0.01, options.minZoomRatio);
2104 | var maxZoomRatio = Math.min(100, options.maxZoomRatio);
2105 | ratio = Math.min(Math.max(ratio, minZoomRatio), maxZoomRatio);
2106 | }
2107 |
2108 | if (_originalEvent && ratio > 0.95 && ratio < 1.05) {
2109 | ratio = 1;
2110 | }
2111 |
2112 | var newWidth = naturalWidth * ratio;
2113 | var newHeight = naturalHeight * ratio;
2114 | var offsetWidth = newWidth - width;
2115 | var offsetHeight = newHeight - height;
2116 | var oldRatio = width / naturalWidth;
2117 |
2118 | if (isFunction(options.zoom)) {
2119 | addListener(element, EVENT_ZOOM, options.zoom, {
2120 | once: true
2121 | });
2122 | }
2123 |
2124 | if (dispatchEvent(element, EVENT_ZOOM, {
2125 | ratio: ratio,
2126 | oldRatio: oldRatio,
2127 | originalEvent: _originalEvent
2128 | }) === false) {
2129 | return this;
2130 | }
2131 |
2132 | this.zooming = true;
2133 |
2134 | if (_originalEvent) {
2135 | var offset = getOffset(this.viewer);
2136 | var center = pointers && Object.keys(pointers).length ? getPointersCenter(pointers) : {
2137 | pageX: _originalEvent.pageX,
2138 | pageY: _originalEvent.pageY
2139 | }; // Zoom from the triggering point of the event
2140 |
2141 | imageData.left -= offsetWidth * ((center.pageX - offset.left - left) / width);
2142 | imageData.top -= offsetHeight * ((center.pageY - offset.top - top) / height);
2143 | } else {
2144 | // Zoom from the center of the image
2145 | imageData.left -= offsetWidth / 2;
2146 | imageData.top -= offsetHeight / 2;
2147 | }
2148 |
2149 | imageData.width = newWidth;
2150 | imageData.height = newHeight;
2151 | imageData.ratio = ratio;
2152 | this.renderImage(function () {
2153 | _this2.zooming = false;
2154 |
2155 | if (isFunction(options.zoomed)) {
2156 | addListener(element, EVENT_ZOOMED, options.zoomed, {
2157 | once: true
2158 | });
2159 | }
2160 |
2161 | dispatchEvent(element, EVENT_ZOOMED, {
2162 | ratio: ratio,
2163 | oldRatio: oldRatio,
2164 | originalEvent: _originalEvent
2165 | });
2166 | });
2167 |
2168 | if (hasTooltip) {
2169 | this.tooltip();
2170 | }
2171 | }
2172 |
2173 | return this;
2174 | },
2175 |
2176 | /**
2177 | * Rotate the image with a relative degree.
2178 | * @param {number} degree - The rotate degree.
2179 | * @returns {Viewer} this
2180 | */
2181 | rotate: function rotate(degree) {
2182 | this.rotateTo((this.imageData.rotate || 0) + Number(degree));
2183 | return this;
2184 | },
2185 |
2186 | /**
2187 | * Rotate the image to an absolute degree.
2188 | * @param {number} degree - The rotate degree.
2189 | * @returns {Viewer} this
2190 | */
2191 | rotateTo: function rotateTo(degree) {
2192 | var imageData = this.imageData;
2193 | degree = Number(degree);
2194 |
2195 | if (isNumber(degree) && this.viewed && !this.played && this.options.rotatable) {
2196 | imageData.rotate = degree;
2197 | this.renderImage();
2198 | }
2199 |
2200 | return this;
2201 | },
2202 |
2203 | /**
2204 | * Scale the image on the x-axis.
2205 | * @param {number} scaleX - The scale ratio on the x-axis.
2206 | * @returns {Viewer} this
2207 | */
2208 | scaleX: function scaleX(_scaleX) {
2209 | this.scale(_scaleX, this.imageData.scaleY);
2210 | return this;
2211 | },
2212 |
2213 | /**
2214 | * Scale the image on the y-axis.
2215 | * @param {number} scaleY - The scale ratio on the y-axis.
2216 | * @returns {Viewer} this
2217 | */
2218 | scaleY: function scaleY(_scaleY) {
2219 | this.scale(this.imageData.scaleX, _scaleY);
2220 | return this;
2221 | },
2222 |
2223 | /**
2224 | * Scale the image.
2225 | * @param {number} scaleX - The scale ratio on the x-axis.
2226 | * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
2227 | * @returns {Viewer} this
2228 | */
2229 | scale: function scale(scaleX) {
2230 | var scaleY = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : scaleX;
2231 | var imageData = this.imageData;
2232 | scaleX = Number(scaleX);
2233 | scaleY = Number(scaleY);
2234 |
2235 | if (this.viewed && !this.played && this.options.scalable) {
2236 | var changed = false;
2237 |
2238 | if (isNumber(scaleX)) {
2239 | imageData.scaleX = scaleX;
2240 | changed = true;
2241 | }
2242 |
2243 | if (isNumber(scaleY)) {
2244 | imageData.scaleY = scaleY;
2245 | changed = true;
2246 | }
2247 |
2248 | if (changed) {
2249 | this.renderImage();
2250 | }
2251 | }
2252 |
2253 | return this;
2254 | },
2255 |
2256 | /**
2257 | * Play the images
2258 | * @param {boolean} [fullscreen=false] - Indicate if request fullscreen or not.
2259 | * @returns {Viewer} this
2260 | */
2261 | play: function play() {
2262 | var _this3 = this;
2263 |
2264 | var fullscreen = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
2265 |
2266 | if (!this.isShown || this.played) {
2267 | return this;
2268 | }
2269 |
2270 | var options = this.options,
2271 | player = this.player;
2272 | var onLoad = this.loadImage.bind(this);
2273 | var list = [];
2274 | var total = 0;
2275 | var index = 0;
2276 | this.played = true;
2277 | this.onLoadWhenPlay = onLoad;
2278 |
2279 | if (fullscreen) {
2280 | this.requestFullscreen();
2281 | }
2282 |
2283 | addClass(player, CLASS_SHOW);
2284 | forEach(this.items, function (item, i) {
2285 | var img = item.querySelector('img');
2286 | var image = document.createElement('img');
2287 | image.src = getData(img, 'originalUrl');
2288 | image.alt = img.getAttribute('alt');
2289 | total += 1;
2290 | addClass(image, CLASS_FADE);
2291 | toggleClass(image, CLASS_TRANSITION, options.transition);
2292 |
2293 | if (hasClass(item, CLASS_ACTIVE)) {
2294 | addClass(image, CLASS_IN);
2295 | index = i;
2296 | }
2297 |
2298 | list.push(image);
2299 | addListener(image, EVENT_LOAD, onLoad, {
2300 | once: true
2301 | });
2302 | player.appendChild(image);
2303 | });
2304 |
2305 | if (isNumber(options.interval) && options.interval > 0) {
2306 | var play = function play() {
2307 | _this3.playing = setTimeout(function () {
2308 | removeClass(list[index], CLASS_IN);
2309 | index += 1;
2310 | index = index < total ? index : 0;
2311 | addClass(list[index], CLASS_IN);
2312 | play();
2313 | }, options.interval);
2314 | };
2315 |
2316 | if (total > 1) {
2317 | play();
2318 | }
2319 | }
2320 |
2321 | return this;
2322 | },
2323 | // Stop play
2324 | stop: function stop() {
2325 | var _this4 = this;
2326 |
2327 | if (!this.played) {
2328 | return this;
2329 | }
2330 |
2331 | var player = this.player;
2332 | this.played = false;
2333 | clearTimeout(this.playing);
2334 | forEach(player.getElementsByTagName('img'), function (image) {
2335 | removeListener(image, EVENT_LOAD, _this4.onLoadWhenPlay);
2336 | });
2337 | removeClass(player, CLASS_SHOW);
2338 | player.innerHTML = '';
2339 | this.exitFullscreen();
2340 | return this;
2341 | },
2342 | // Enter modal mode (only available in inline mode)
2343 | full: function full() {
2344 | var _this5 = this;
2345 |
2346 | var options = this.options,
2347 | viewer = this.viewer,
2348 | image = this.image,
2349 | list = this.list;
2350 |
2351 | if (!this.isShown || this.played || this.fulled || !options.inline) {
2352 | return this;
2353 | }
2354 |
2355 | this.fulled = true;
2356 | this.open();
2357 | addClass(this.button, CLASS_FULLSCREEN_EXIT);
2358 |
2359 | if (options.transition) {
2360 | removeClass(list, CLASS_TRANSITION);
2361 |
2362 | if (this.viewed) {
2363 | removeClass(image, CLASS_TRANSITION);
2364 | }
2365 | }
2366 |
2367 | addClass(viewer, CLASS_FIXED);
2368 | viewer.setAttribute('style', '');
2369 | setStyle(viewer, {
2370 | zIndex: options.zIndex
2371 | });
2372 | this.initContainer();
2373 | this.viewerData = assign({}, this.containerData);
2374 | this.renderList();
2375 |
2376 | if (this.viewed) {
2377 | this.initImage(function () {
2378 | _this5.renderImage(function () {
2379 | if (options.transition) {
2380 | setTimeout(function () {
2381 | addClass(image, CLASS_TRANSITION);
2382 | addClass(list, CLASS_TRANSITION);
2383 | }, 0);
2384 | }
2385 | });
2386 | });
2387 | }
2388 |
2389 | return this;
2390 | },
2391 | // Exit modal mode (only available in inline mode)
2392 | exit: function exit() {
2393 | var _this6 = this;
2394 |
2395 | var options = this.options,
2396 | viewer = this.viewer,
2397 | image = this.image,
2398 | list = this.list;
2399 |
2400 | if (!this.isShown || this.played || !this.fulled || !options.inline) {
2401 | return this;
2402 | }
2403 |
2404 | this.fulled = false;
2405 | this.close();
2406 | removeClass(this.button, CLASS_FULLSCREEN_EXIT);
2407 |
2408 | if (options.transition) {
2409 | removeClass(list, CLASS_TRANSITION);
2410 |
2411 | if (this.viewed) {
2412 | removeClass(image, CLASS_TRANSITION);
2413 | }
2414 | }
2415 |
2416 | removeClass(viewer, CLASS_FIXED);
2417 | setStyle(viewer, {
2418 | zIndex: options.zIndexInline
2419 | });
2420 | this.viewerData = assign({}, this.parentData);
2421 | this.renderViewer();
2422 | this.renderList();
2423 |
2424 | if (this.viewed) {
2425 | this.initImage(function () {
2426 | _this6.renderImage(function () {
2427 | if (options.transition) {
2428 | setTimeout(function () {
2429 | addClass(image, CLASS_TRANSITION);
2430 | addClass(list, CLASS_TRANSITION);
2431 | }, 0);
2432 | }
2433 | });
2434 | });
2435 | }
2436 |
2437 | return this;
2438 | },
2439 | // Show the current ratio of the image with percentage
2440 | tooltip: function tooltip() {
2441 | var _this7 = this;
2442 |
2443 | var options = this.options,
2444 | tooltipBox = this.tooltipBox,
2445 | imageData = this.imageData;
2446 |
2447 | if (!this.viewed || this.played || !options.tooltip) {
2448 | return this;
2449 | }
2450 |
2451 | tooltipBox.textContent = "".concat(Math.round(imageData.ratio * 100), "%");
2452 |
2453 | if (!this.tooltipping) {
2454 | if (options.transition) {
2455 | if (this.fading) {
2456 | dispatchEvent(tooltipBox, EVENT_TRANSITION_END);
2457 | }
2458 |
2459 | addClass(tooltipBox, CLASS_SHOW);
2460 | addClass(tooltipBox, CLASS_FADE);
2461 | addClass(tooltipBox, CLASS_TRANSITION); // Force reflow to enable CSS3 transition
2462 |
2463 | tooltipBox.initialOffsetWidth = tooltipBox.offsetWidth;
2464 | addClass(tooltipBox, CLASS_IN);
2465 | } else {
2466 | addClass(tooltipBox, CLASS_SHOW);
2467 | }
2468 | } else {
2469 | clearTimeout(this.tooltipping);
2470 | }
2471 |
2472 | this.tooltipping = setTimeout(function () {
2473 | if (options.transition) {
2474 | addListener(tooltipBox, EVENT_TRANSITION_END, function () {
2475 | removeClass(tooltipBox, CLASS_SHOW);
2476 | removeClass(tooltipBox, CLASS_FADE);
2477 | removeClass(tooltipBox, CLASS_TRANSITION);
2478 | _this7.fading = false;
2479 | }, {
2480 | once: true
2481 | });
2482 | removeClass(tooltipBox, CLASS_IN);
2483 | _this7.fading = true;
2484 | } else {
2485 | removeClass(tooltipBox, CLASS_SHOW);
2486 | }
2487 |
2488 | _this7.tooltipping = false;
2489 | }, 1000);
2490 | return this;
2491 | },
2492 | // Toggle the image size between its natural size and initial size
2493 | toggle: function toggle() {
2494 | if (this.imageData.ratio === 1) {
2495 | this.zoomTo(this.initialImageData.ratio, true);
2496 | } else {
2497 | this.zoomTo(1, true);
2498 | }
2499 |
2500 | return this;
2501 | },
2502 | // Reset the image to its initial state
2503 | reset: function reset() {
2504 | if (this.viewed && !this.played) {
2505 | this.imageData = assign({}, this.initialImageData);
2506 | this.renderImage();
2507 | }
2508 |
2509 | return this;
2510 | },
2511 | // Update viewer when images changed
2512 | update: function update() {
2513 | var element = this.element,
2514 | options = this.options,
2515 | isImg = this.isImg; // Destroy viewer if the target image was deleted
2516 |
2517 | if (isImg && !element.parentNode) {
2518 | return this.destroy();
2519 | }
2520 |
2521 | var images = [];
2522 | forEach(isImg ? [element] : element.querySelectorAll('img'), function (image) {
2523 | if (options.filter) {
2524 | if (options.filter(image)) {
2525 | images.push(image);
2526 | }
2527 | } else {
2528 | images.push(image);
2529 | }
2530 | });
2531 |
2532 | if (!images.length) {
2533 | return this;
2534 | }
2535 |
2536 | this.images = images;
2537 | this.length = images.length;
2538 |
2539 | if (this.ready) {
2540 | var indexes = [];
2541 | forEach(this.items, function (item, i) {
2542 | var img = item.querySelector('img');
2543 | var image = images[i];
2544 |
2545 | if (image && img) {
2546 | if (image.src !== img.src) {
2547 | indexes.push(i);
2548 | }
2549 | } else {
2550 | indexes.push(i);
2551 | }
2552 | });
2553 | setStyle(this.list, {
2554 | width: 'auto'
2555 | });
2556 | this.initList();
2557 |
2558 | if (this.isShown) {
2559 | if (this.length) {
2560 | if (this.viewed) {
2561 | var index = indexes.indexOf(this.index);
2562 |
2563 | if (index >= 0) {
2564 | this.viewed = false;
2565 | this.view(Math.max(this.index - (index + 1), 0));
2566 | } else {
2567 | addClass(this.items[this.index], CLASS_ACTIVE);
2568 | }
2569 | }
2570 | } else {
2571 | this.image = null;
2572 | this.viewed = false;
2573 | this.index = 0;
2574 | this.imageData = {};
2575 | this.canvas.innerHTML = '';
2576 | this.title.innerHTML = '';
2577 | }
2578 | }
2579 | } else {
2580 | this.build();
2581 | }
2582 |
2583 | return this;
2584 | },
2585 | // Destroy the viewer
2586 | destroy: function destroy() {
2587 | var element = this.element,
2588 | options = this.options;
2589 |
2590 | if (!element[NAMESPACE]) {
2591 | return this;
2592 | }
2593 |
2594 | this.destroyed = true;
2595 |
2596 | if (this.ready) {
2597 | if (this.played) {
2598 | this.stop();
2599 | }
2600 |
2601 | if (options.inline) {
2602 | if (this.fulled) {
2603 | this.exit();
2604 | }
2605 |
2606 | this.unbind();
2607 | } else if (this.isShown) {
2608 | if (this.viewing) {
2609 | if (this.imageRendering) {
2610 | this.imageRendering.abort();
2611 | } else if (this.imageInitializing) {
2612 | this.imageInitializing.abort();
2613 | }
2614 | }
2615 |
2616 | if (this.hiding) {
2617 | this.transitioning.abort();
2618 | }
2619 |
2620 | this.hidden();
2621 | } else if (this.showing) {
2622 | this.transitioning.abort();
2623 | this.hidden();
2624 | }
2625 |
2626 | this.ready = false;
2627 | this.viewer.parentNode.removeChild(this.viewer);
2628 | } else if (options.inline) {
2629 | if (this.delaying) {
2630 | this.delaying.abort();
2631 | } else if (this.initializing) {
2632 | this.initializing.abort();
2633 | }
2634 | }
2635 |
2636 | if (!options.inline) {
2637 | removeListener(element, EVENT_CLICK, this.onStart);
2638 | }
2639 |
2640 | element[NAMESPACE] = undefined;
2641 | return this;
2642 | }
2643 | };
2644 |
2645 | var others = {
2646 | open: function open() {
2647 | var body = this.body;
2648 | addClass(body, CLASS_OPEN);
2649 | body.style.paddingRight = "".concat(this.scrollbarWidth + (parseFloat(this.initialBodyPaddingRight) || 0), "px");
2650 | },
2651 | close: function close() {
2652 | var body = this.body;
2653 | removeClass(body, CLASS_OPEN);
2654 | body.style.paddingRight = this.initialBodyPaddingRight;
2655 | },
2656 | shown: function shown() {
2657 | var element = this.element,
2658 | options = this.options;
2659 | this.fulled = true;
2660 | this.isShown = true;
2661 | this.render();
2662 | this.bind();
2663 | this.showing = false;
2664 |
2665 | if (isFunction(options.shown)) {
2666 | addListener(element, EVENT_SHOWN, options.shown, {
2667 | once: true
2668 | });
2669 | }
2670 |
2671 | if (dispatchEvent(element, EVENT_SHOWN) === false) {
2672 | return;
2673 | }
2674 |
2675 | if (this.ready && this.isShown && !this.hiding) {
2676 | this.view(this.index);
2677 | }
2678 | },
2679 | hidden: function hidden() {
2680 | var element = this.element,
2681 | options = this.options;
2682 | this.fulled = false;
2683 | this.viewed = false;
2684 | this.isShown = false;
2685 | this.close();
2686 | this.unbind();
2687 | addClass(this.viewer, CLASS_HIDE);
2688 | this.resetList();
2689 | this.resetImage();
2690 | this.hiding = false;
2691 |
2692 | if (!this.destroyed) {
2693 | if (isFunction(options.hidden)) {
2694 | addListener(element, EVENT_HIDDEN, options.hidden, {
2695 | once: true
2696 | });
2697 | }
2698 |
2699 | dispatchEvent(element, EVENT_HIDDEN);
2700 | }
2701 | },
2702 | requestFullscreen: function requestFullscreen() {
2703 | var document = this.element.ownerDocument;
2704 |
2705 | if (this.fulled && !(document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2706 | var documentElement = document.documentElement; // Element.requestFullscreen()
2707 |
2708 | if (documentElement.requestFullscreen) {
2709 | documentElement.requestFullscreen();
2710 | } else if (documentElement.webkitRequestFullscreen) {
2711 | documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
2712 | } else if (documentElement.mozRequestFullScreen) {
2713 | documentElement.mozRequestFullScreen();
2714 | } else if (documentElement.msRequestFullscreen) {
2715 | documentElement.msRequestFullscreen();
2716 | }
2717 | }
2718 | },
2719 | exitFullscreen: function exitFullscreen() {
2720 | var document = this.element.ownerDocument;
2721 |
2722 | if (this.fulled && (document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement)) {
2723 | // Document.exitFullscreen()
2724 | if (document.exitFullscreen) {
2725 | document.exitFullscreen();
2726 | } else if (document.webkitExitFullscreen) {
2727 | document.webkitExitFullscreen();
2728 | } else if (document.mozCancelFullScreen) {
2729 | document.mozCancelFullScreen();
2730 | } else if (document.msExitFullscreen) {
2731 | document.msExitFullscreen();
2732 | }
2733 | }
2734 | },
2735 | change: function change(event) {
2736 | var options = this.options,
2737 | pointers = this.pointers;
2738 | var pointer = pointers[Object.keys(pointers)[0]];
2739 | var offsetX = pointer.endX - pointer.startX;
2740 | var offsetY = pointer.endY - pointer.startY;
2741 |
2742 | switch (this.action) {
2743 | // Move the current image
2744 | case ACTION_MOVE:
2745 | this.move(offsetX, offsetY);
2746 | break;
2747 | // Zoom the current image
2748 |
2749 | case ACTION_ZOOM:
2750 | this.zoom(getMaxZoomRatio(pointers), false, event);
2751 | break;
2752 |
2753 | case ACTION_SWITCH:
2754 | {
2755 | this.action = 'switched';
2756 | var absoluteOffsetX = Math.abs(offsetX);
2757 |
2758 | if (absoluteOffsetX > 1 && absoluteOffsetX > Math.abs(offsetY)) {
2759 | // Empty `pointers` as `touchend` event will not be fired after swiped in iOS browsers.
2760 | this.pointers = {};
2761 |
2762 | if (offsetX > 1) {
2763 | this.prev(options.loop);
2764 | } else if (offsetX < -1) {
2765 | this.next(options.loop);
2766 | }
2767 | }
2768 |
2769 | break;
2770 | }
2771 | } // Override
2772 |
2773 |
2774 | forEach(pointers, function (p) {
2775 | p.startX = p.endX;
2776 | p.startY = p.endY;
2777 | });
2778 | },
2779 | isSwitchable: function isSwitchable() {
2780 | var imageData = this.imageData,
2781 | viewerData = this.viewerData;
2782 | return this.length > 1 && imageData.left >= 0 && imageData.top >= 0 && imageData.width <= viewerData.width && imageData.height <= viewerData.height;
2783 | }
2784 | };
2785 |
2786 | var AnotherViewer = WINDOW.Viewer;
2787 |
2788 | var Viewer =
2789 | /*#__PURE__*/
2790 | function () {
2791 | /**
2792 | * Create a new Viewer.
2793 | * @param {Element} element - The target element for viewing.
2794 | * @param {Object} [options={}] - The configuration options.
2795 | */
2796 | function Viewer(element) {
2797 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2798 |
2799 | _classCallCheck(this, Viewer);
2800 |
2801 | if (!element || element.nodeType !== 1) {
2802 | throw new Error('The first argument is required and must be an element.');
2803 | }
2804 |
2805 | this.element = element;
2806 | this.options = assign({}, DEFAULTS, isPlainObject(options) && options);
2807 | this.action = false;
2808 | this.fading = false;
2809 | this.fulled = false;
2810 | this.hiding = false;
2811 | this.imageClicked = false;
2812 | this.imageData = {};
2813 | this.index = this.options.initialViewIndex;
2814 | this.isImg = false;
2815 | this.isShown = false;
2816 | this.length = 0;
2817 | this.played = false;
2818 | this.playing = false;
2819 | this.pointers = {};
2820 | this.ready = false;
2821 | this.showing = false;
2822 | this.timeout = false;
2823 | this.tooltipping = false;
2824 | this.viewed = false;
2825 | this.viewing = false;
2826 | this.wheeling = false;
2827 | this.zooming = false;
2828 | this.init();
2829 | }
2830 |
2831 | _createClass(Viewer, [{
2832 | key: "init",
2833 | value: function init() {
2834 | var _this = this;
2835 |
2836 | var element = this.element,
2837 | options = this.options;
2838 |
2839 | if (element[NAMESPACE]) {
2840 | return;
2841 | }
2842 |
2843 | element[NAMESPACE] = this;
2844 | var isImg = element.tagName.toLowerCase() === 'img';
2845 | var images = [];
2846 | forEach(isImg ? [element] : element.querySelectorAll('img'), function (image) {
2847 | if (isFunction(options.filter)) {
2848 | if (options.filter.call(_this, image)) {
2849 | images.push(image);
2850 | }
2851 | } else {
2852 | images.push(image);
2853 | }
2854 | });
2855 | this.isImg = isImg;
2856 | this.length = images.length;
2857 | this.images = images;
2858 | var ownerDocument = element.ownerDocument;
2859 | var body = ownerDocument.body || ownerDocument.documentElement;
2860 | this.body = body;
2861 | this.scrollbarWidth = window.innerWidth - ownerDocument.documentElement.clientWidth;
2862 | this.initialBodyPaddingRight = window.getComputedStyle(body).paddingRight; // Override `transition` option if it is not supported
2863 |
2864 | if (isUndefined(document.createElement(NAMESPACE).style.transition)) {
2865 | options.transition = false;
2866 | }
2867 |
2868 | if (options.inline) {
2869 | var count = 0;
2870 |
2871 | var progress = function progress() {
2872 | count += 1;
2873 |
2874 | if (count === _this.length) {
2875 | var timeout;
2876 | _this.initializing = false;
2877 | _this.delaying = {
2878 | abort: function abort() {
2879 | clearTimeout(timeout);
2880 | }
2881 | }; // build asynchronously to keep `this.viewer` is accessible in `ready` event handler.
2882 |
2883 | timeout = setTimeout(function () {
2884 | _this.delaying = false;
2885 |
2886 | _this.build();
2887 | }, 0);
2888 | }
2889 | };
2890 |
2891 | this.initializing = {
2892 | abort: function abort() {
2893 | forEach(images, function (image) {
2894 | if (!image.complete) {
2895 | removeListener(image, EVENT_LOAD, progress);
2896 | }
2897 | });
2898 | }
2899 | };
2900 | forEach(images, function (image) {
2901 | if (image.complete) {
2902 | progress();
2903 | } else {
2904 | addListener(image, EVENT_LOAD, progress, {
2905 | once: true
2906 | });
2907 | }
2908 | });
2909 | } else {
2910 | addListener(element, EVENT_CLICK, this.onStart = function (_ref) {
2911 | var target = _ref.target;
2912 |
2913 | if (target.tagName.toLowerCase() === 'img' && (!isFunction(options.filter) || options.filter.call(_this, target))) {
2914 | _this.view(_this.images.indexOf(target));
2915 | }
2916 | });
2917 | }
2918 | }
2919 | }, {
2920 | key: "build",
2921 | value: function build() {
2922 | if (this.ready) {
2923 | return;
2924 | }
2925 |
2926 | var element = this.element,
2927 | options = this.options;
2928 | var parent = element.parentNode;
2929 | var template = document.createElement('div');
2930 | template.innerHTML = TEMPLATE;
2931 | var viewer = template.querySelector(".".concat(NAMESPACE, "-container"));
2932 | var title = viewer.querySelector(".".concat(NAMESPACE, "-title"));
2933 | var toolbar = viewer.querySelector(".".concat(NAMESPACE, "-toolbar"));
2934 | var navbar = viewer.querySelector(".".concat(NAMESPACE, "-navbar"));
2935 | var button = viewer.querySelector(".".concat(NAMESPACE, "-button"));
2936 | var canvas = viewer.querySelector(".".concat(NAMESPACE, "-canvas"));
2937 | this.parent = parent;
2938 | this.viewer = viewer;
2939 | this.title = title;
2940 | this.toolbar = toolbar;
2941 | this.navbar = navbar;
2942 | this.button = button;
2943 | this.canvas = canvas;
2944 | this.footer = viewer.querySelector(".".concat(NAMESPACE, "-footer"));
2945 | this.tooltipBox = viewer.querySelector(".".concat(NAMESPACE, "-tooltip"));
2946 | this.player = viewer.querySelector(".".concat(NAMESPACE, "-player"));
2947 | this.list = viewer.querySelector(".".concat(NAMESPACE, "-list"));
2948 | addClass(title, !options.title ? CLASS_HIDE : getResponsiveClass(Array.isArray(options.title) ? options.title[0] : options.title));
2949 | addClass(navbar, !options.navbar ? CLASS_HIDE : getResponsiveClass(options.navbar));
2950 | toggleClass(button, CLASS_HIDE, !options.button);
2951 |
2952 | if (options.backdrop) {
2953 | addClass(viewer, "".concat(NAMESPACE, "-backdrop"));
2954 |
2955 | if (!options.inline && options.backdrop !== 'static') {
2956 | setData(canvas, DATA_ACTION, 'hide');
2957 | }
2958 | }
2959 |
2960 | if (isString(options.className) && options.className) {
2961 | // In case there are multiple class names
2962 | options.className.split(REGEXP_SPACES).forEach(function (className) {
2963 | addClass(viewer, className);
2964 | });
2965 | }
2966 |
2967 | if (options.toolbar) {
2968 | var list = document.createElement('ul');
2969 | var custom = isPlainObject(options.toolbar);
2970 | var zoomButtons = BUTTONS.slice(0, 3);
2971 | var rotateButtons = BUTTONS.slice(7, 9);
2972 | var scaleButtons = BUTTONS.slice(9);
2973 |
2974 | if (!custom) {
2975 | addClass(toolbar, getResponsiveClass(options.toolbar));
2976 | }
2977 |
2978 | forEach(custom ? options.toolbar : BUTTONS, function (value, index) {
2979 | var deep = custom && isPlainObject(value);
2980 | var name = custom ? hyphenate(index) : value;
2981 | var show = deep && !isUndefined(value.show) ? value.show : value;
2982 |
2983 | if (!show || !options.zoomable && zoomButtons.indexOf(name) !== -1 || !options.rotatable && rotateButtons.indexOf(name) !== -1 || !options.scalable && scaleButtons.indexOf(name) !== -1) {
2984 | return;
2985 | }
2986 |
2987 | var size = deep && !isUndefined(value.size) ? value.size : value;
2988 | var click = deep && !isUndefined(value.click) ? value.click : value;
2989 | var item = document.createElement('li');
2990 | item.setAttribute('role', 'button');
2991 | addClass(item, "".concat(NAMESPACE, "-").concat(name));
2992 |
2993 | if (!isFunction(click)) {
2994 | setData(item, DATA_ACTION, name);
2995 | }
2996 |
2997 | if (isNumber(show)) {
2998 | addClass(item, getResponsiveClass(show));
2999 | }
3000 |
3001 | if (['small', 'large'].indexOf(size) !== -1) {
3002 | addClass(item, "".concat(NAMESPACE, "-").concat(size));
3003 | } else if (name === 'play') {
3004 | addClass(item, "".concat(NAMESPACE, "-large"));
3005 | }
3006 |
3007 | if (isFunction(click)) {
3008 | addListener(item, EVENT_CLICK, click);
3009 | }
3010 |
3011 | list.appendChild(item);
3012 | });
3013 | toolbar.appendChild(list);
3014 | } else {
3015 | addClass(toolbar, CLASS_HIDE);
3016 | }
3017 |
3018 | if (!options.rotatable) {
3019 | var rotates = toolbar.querySelectorAll('li[class*="rotate"]');
3020 | addClass(rotates, CLASS_INVISIBLE);
3021 | forEach(rotates, function (rotate) {
3022 | toolbar.appendChild(rotate);
3023 | });
3024 | }
3025 |
3026 | if (options.inline) {
3027 | addClass(button, CLASS_FULLSCREEN);
3028 | setStyle(viewer, {
3029 | zIndex: options.zIndexInline
3030 | });
3031 |
3032 | if (window.getComputedStyle(parent).position === 'static') {
3033 | setStyle(parent, {
3034 | position: 'relative'
3035 | });
3036 | }
3037 |
3038 | parent.insertBefore(viewer, element.nextSibling);
3039 | } else {
3040 | addClass(button, CLASS_CLOSE);
3041 | addClass(viewer, CLASS_FIXED);
3042 | addClass(viewer, CLASS_FADE);
3043 | addClass(viewer, CLASS_HIDE);
3044 | setStyle(viewer, {
3045 | zIndex: options.zIndex
3046 | });
3047 | var container = options.container;
3048 |
3049 | if (isString(container)) {
3050 | container = element.ownerDocument.querySelector(container);
3051 | }
3052 |
3053 | if (!container) {
3054 | container = this.body;
3055 | }
3056 |
3057 | container.appendChild(viewer);
3058 | }
3059 |
3060 | if (options.inline) {
3061 | this.render();
3062 | this.bind();
3063 | this.isShown = true;
3064 | }
3065 |
3066 | this.ready = true;
3067 |
3068 | if (isFunction(options.ready)) {
3069 | addListener(element, EVENT_READY, options.ready, {
3070 | once: true
3071 | });
3072 | }
3073 |
3074 | if (dispatchEvent(element, EVENT_READY) === false) {
3075 | this.ready = false;
3076 | return;
3077 | }
3078 |
3079 | if (this.ready && options.inline) {
3080 | this.view(this.index);
3081 | }
3082 | }
3083 | /**
3084 | * Get the no conflict viewer class.
3085 | * @returns {Viewer} The viewer class.
3086 | */
3087 |
3088 | }], [{
3089 | key: "noConflict",
3090 | value: function noConflict() {
3091 | window.Viewer = AnotherViewer;
3092 | return Viewer;
3093 | }
3094 | /**
3095 | * Change the default options.
3096 | * @param {Object} options - The new default options.
3097 | */
3098 |
3099 | }, {
3100 | key: "setDefaults",
3101 | value: function setDefaults(options) {
3102 | assign(DEFAULTS, isPlainObject(options) && options);
3103 | }
3104 | }]);
3105 |
3106 | return Viewer;
3107 | }();
3108 |
3109 | assign(Viewer.prototype, render, events, handlers, methods, others);
3110 |
3111 | if ($.fn) {
3112 | var AnotherViewer$1 = $.fn.viewer;
3113 | var NAMESPACE$1 = 'viewer';
3114 |
3115 | $.fn.viewer = function jQueryViewer(option) {
3116 | for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
3117 | args[_key - 1] = arguments[_key];
3118 | }
3119 |
3120 | var result;
3121 | this.each(function (i, element) {
3122 | var $element = $(element);
3123 | var isDestroy = option === 'destroy';
3124 | var viewer = $element.data(NAMESPACE$1);
3125 |
3126 | if (!viewer) {
3127 | if (isDestroy) {
3128 | return;
3129 | }
3130 |
3131 | var options = $.extend({}, $element.data(), $.isPlainObject(option) && option);
3132 | viewer = new Viewer(element, options);
3133 | $element.data(NAMESPACE$1, viewer);
3134 | }
3135 |
3136 | if (typeof option === 'string') {
3137 | var fn = viewer[option];
3138 |
3139 | if ($.isFunction(fn)) {
3140 | result = fn.apply(viewer, args);
3141 |
3142 | if (result === viewer) {
3143 | result = undefined;
3144 | }
3145 |
3146 | if (isDestroy) {
3147 | $element.removeData(NAMESPACE$1);
3148 | }
3149 | }
3150 | }
3151 | });
3152 | return result !== undefined ? result : this;
3153 | };
3154 |
3155 | $.fn.viewer.Constructor = Viewer;
3156 | $.fn.viewer.setDefaults = Viewer.setDefaults;
3157 |
3158 | $.fn.viewer.noConflict = function noConflict() {
3159 | $.fn.viewer = AnotherViewer$1;
3160 | return this;
3161 | };
3162 | }
3163 |
--------------------------------------------------------------------------------