34

Imageinspector

Objective
  • Responder
    • View
      • Inspector
        • ImageInspector

Click in the image. Select a JPG, PNG, GIF or SVG file. NOTE: The maximum size of the file is configured to 1 MB and the maximum width of the display to 320 px. Move the pointer of the mouse over the image to display its dimensions. Open a folder containing images with the explorer of your file system. Drag and drop a JPG, PNG, GIF or SVG file over the image.

Try saving on your disk and loading the logo of Chrome, of Firefox or the acronym in SVG. NOTE: An SVG doesn't have a maximum size and its dimensions depend on the maximum width of the display (320 px).

To start loading an image from a file with a button, a code can run the method loadImage of the inspector when the button is clicked. IMPORTANT: For security reasons, loadImage must be called in response to a user interaction.

<p class="noprint"><button id="btn_load_image" type="submit" class="narrow" title=""><i class="fa fa-upload"></i></button></p>

And in JavaScript:

document.getElementById('btn_load_image').onclick = () => inspector.loadImage();
  1. function ImageInspector(value = null, options = false) {
  2.     options = options || {};
  3.  
  4.     let filetypes = options.filetypes;
  5.     let maxsize = options.maxsize;
  6.  
  7.     let draganddrop = options.draganddrop === undefined ? true : (options.draganddrop ? true : false);
  8.     let loadonclick = options.loadonclick === undefined ? true : (options.loadonclick ? true : false);
  9.  
  10.     if (!this.validate(value))
  11.         throw new TypeError();
  12.  
  13.     if (filetypes === undefined)
  14.         filetypes = ImageInspector.defaultFileTypes;
  15.     else if (! (Array.isArray(filetypes) && filetypes.length > 0 && filetypes.every((e) => typeof e === 'string')))
  16.         throw new TypeError();
  17.  
  18.     if (maxsize === undefined)
  19.         maxsize = 0;
  20.     else if (typeof maxsize !== 'number')
  21.         throw new TypeError();
  22.     else if (maxsize < 0)
  23.         throw new RangeError();
  24.  
  25.     Inspector.call(this);
  26.  
  27.     if (value !== null) {
  28.         const [data, width, height] = value;
  29.  
  30.         this._value = data;
  31.         this._width = width;
  32.         this._height = height;
  33.     }
  34.  
  35.     this._type = undefined;
  36.     this._size = 0;
  37.  
  38.     this._filetypes = filetypes;
  39.     this._maxsize = maxsize;
  40.  
  41.     this._draganddrop = draganddrop;
  42.     this._loadonclick = loadonclick;
  43.  
  44.     this._imageWidget = null;
  45.     this._imageWidgetSrc = null;
  46. }
  47.  
  48. ImageInspector.prototype = Object.create(Inspector.prototype);
  49.  
  50. Object.defineProperty(ImageInspector.prototype, 'constructor', { value: ImageInspector, enumerable: false, writable: true });
  51.  
  52. ImageInspector.defaultFileTypes = ['image/jpeg', 'image/png', 'image/gif'];
  53.  
  54. Object.defineProperty(ImageInspector.prototype, 'width', {
  55.     get:    function() {
  56.         return this._width;
  57.     }
  58. });
  59.  
  60. Object.defineProperty(ImageInspector.prototype, 'height', {
  61.     get:    function() {
  62.         return this._height;
  63.     }
  64. });
  65.  
  66. Object.defineProperty(ImageInspector.prototype, 'type', {
  67.     get:    function() {
  68.         if (!this._value)
  69.             return undefined;
  70.  
  71.         if (this._type === undefined)
  72.             this._type = /^data:(image\/[-.+0-9a-zA-Z]+);base64,/.exec(this._value)[1];
  73.  
  74.         return this._type;
  75.     }
  76. });
  77.  
  78. Object.defineProperty(ImageInspector.prototype, 'size', {
  79.     get:    function() {
  80.         if (!this._value)
  81.             return 0;
  82.  
  83.         if (!this._size)
  84.             this._size = atob(this._value.substring(this._value.indexOf(',')+1)).length;
  85.  
  86.         return this._size;
  87.     }
  88. });
  89.  
  90. ImageInspector.prototype.validate = function(val) {
  91.     return val === null || (Array.isArray(val) && Validator.validateImageDataURL(val[0]) && Number.isInteger(val[1]) && Number.isInteger(val[2]) && val[1] >= 0 && val[2] >= 0);
  92. };
  93.  
  94. ImageInspector.prototype.get = function() {
  95.     return this._value ? [this._value, this._width, this._height] : null;
  96. };
  97.  
  98. ImageInspector.prototype.set = function(val) {
  99.     if (!this.validate(val))
  100.         return false;
  101.  
  102.     const [data, width, height] = val === null ? [null, 0, 0] : val;
  103.  
  104.     if (this._value !== data) {
  105.         this._value = data;
  106.  
  107.         this._width = width;
  108.         this._height = height;
  109.         this._type = undefined;
  110.         this._size = 0;
  111.  
  112.         if (this.interfaced())
  113.             this.resetWidget();
  114.     }
  115.  
  116.     return true;
  117. };
  118.  
  119. ImageInspector.prototype.loadImage = function() {
  120.     if (this._widget)
  121.         this._widget.click();
  122.  
  123.     return this;
  124. };
  125.  
  126. ImageInspector.prototype.reset = function() {
  127.     if (!this._widget)
  128.         return false;
  129.  
  130.     if (!this._imageWidget.src)
  131.         return false;
  132.  
  133.     this._loadurl(this._imageWidget.src);
  134.  
  135.     return true;
  136. };
  137.  
  138. ImageInspector.prototype.disable = function() {
  139.     Inspector.prototype.disable.call(this);
  140.  
  141.     if (this._imageWidget && this._loadonclick)
  142.         this._imageWidget.style.cursor = 'default';
  143.  
  144.     return this;
  145. };
  146.  
  147. ImageInspector.prototype.enable = function() {
  148.     Inspector.prototype.enable.call(this);
  149.  
  150.     if (this._imageWidget && this._loadonclick)
  151.         this._imageWidget.style.cursor = 'pointer';
  152.  
  153.     return this;
  154. };
  155.  
  156. ImageInspector.prototype.changeCallback = function(e) {
  157.     let val = e.target.value ? e.target.files[0] : false;
  158.  
  159.     if (val)
  160.         this._loadblob(e.target.files[0]);
  161. };
  162.  
  163. ImageInspector.prototype.resetWidget = function() {
  164.     if (this._imageWidget) {
  165.         this._imageWidget.src = this._value || this._imageWidgetSrc;
  166.  
  167.         this._imageWidget.setAttribute('title', this._value ? `${this._width}x${this._height}` : '');
  168.     }
  169.  
  170.     return this;
  171. };
  172.  
  173. ImageInspector.prototype.setWidget = function(w) {
  174.     if (w.tagName != 'IMG')
  175.         throw new TypeError();
  176.  
  177.     let input = document.createElement('input');
  178.  
  179.     input.type = 'file';
  180.     input.accept = this._filetypes.join(',');
  181.  
  182.     input.style.display = 'none';
  183.  
  184.     Inspector.prototype.setWidget.call(this, input);
  185.  
  186.     if (this._loadonclick) {
  187.         w.addEventListener('click', () => this.loadImage());
  188.         w.style.cursor = this.isEnabled() ? 'pointer' : 'default';
  189.     }
  190.  
  191.     if (this._draganddrop) {
  192.         w.addEventListener('drop', (e) => {
  193.             const dt = e.dataTransfer;
  194.  
  195.             e.preventDefault();
  196.  
  197.             if (dt.types.indexOf('Files') != -1) {
  198.                 this._loadblob(dt.files[0]);
  199.             }
  200.         });
  201.  
  202.         w.addEventListener('dragenter', (e) => {
  203.             const dt = e.dataTransfer;
  204.  
  205.             if (dt.types.indexOf('Files') != -1) {
  206.                 e.preventDefault();
  207.             }
  208.         });
  209.  
  210.         w.addEventListener('dragleave', (e) => {
  211.             e.preventDefault();
  212.         });
  213.  
  214.         w.addEventListener('dragover', (e) => {
  215.             const dt = e.dataTransfer;
  216.  
  217.             e.preventDefault();
  218.  
  219.             dt.dropEffect = dt.types.indexOf('Files') != -1 ? 'copy' : 'none';
  220.         });
  221.     }
  222.  
  223.     w.removeAttribute('width');
  224.     w.removeAttribute('height');
  225.  
  226.     w.after(input);
  227.  
  228.     this._imageWidget = w;
  229.     this._imageWidgetSrc = w.src;
  230.  
  231.     return this;
  232. };
  233.  
  234. ImageInspector.prototype.manageWidget = function(parent = null) {
  235.     Inspector.prototype.manageWidget.call(this, parent);
  236.  
  237.     if (this._parent && this._imageWidget)
  238.         this._parent.appendChild(this._imageWidget);
  239.  
  240.     return this;
  241. };
  242.  
  243. ImageInspector.prototype.unmanageWidget = function() {
  244.     Inspector.prototype.unmanageWidget.call(this);
  245.  
  246.     if (this._parent && this._imageWidget)
  247.         this._parent.removeChild(this._imageWidget);
  248.  
  249.     return this;
  250. };
  251.  
  252. ImageInspector.prototype.createWidget = function() {
  253.     const html = `<img src="${this._imageWidgetSrc}" alt="" title="" />`;
  254.  
  255.     let template = document.createElement('template');
  256.  
  257.     template.innerHTML = html;
  258.  
  259.     let widget = template.content.children[0];
  260.  
  261.     this.setWidget(widget);
  262.  
  263.     return this;
  264. };
  265.  
  266. ImageInspector.prototype.destroyWidget = function() {
  267.     Inspector.prototype.destroyWidget.call(this);
  268.  
  269.     this._imageWwidget = null;
  270.  
  271.     return this;
  272. };
  273.  
  274. ImageInspector.prototype._loadurl = function(url) {
  275.     const req = new XMLHttpRequest();
  276.  
  277.     req.open('GET', url);
  278.     req.responseType = 'blob';
  279.     req.setRequestHeader = this._filetypes.join(',');
  280.  
  281.     req.onload = () => this._loadblob(req.response);
  282.  
  283.     req.send();
  284. };
  285.  
  286. ImageInspector.prototype._loadblob = function(blob) {
  287.     if (this._maxsize > 0 && blob.size > this._maxsize)
  288.         return;
  289.  
  290.     if (this._filetypes.indexOf(blob.type) == -1)
  291.         return;
  292.  
  293.     const reader = new FileReader();
  294.  
  295.     reader.onload = (e) => {
  296.         let data = e.target.result;
  297.  
  298.         if (this._value !== data) {
  299.             this._value = data;
  300.             this._width = this._height = 0;
  301.             this._type = undefined;
  302.             this._size = blob.size;
  303.  
  304.             if (this._imageWidget) {
  305.                 this._imageWidget.addEventListener('load', (e) => this._onloadsrc(e), { once: true });
  306.                 this._imageWidget.src = data;
  307.             }
  308.         }
  309.     };
  310.  
  311.     reader.readAsDataURL(blob);
  312. };
  313.  
  314. ImageInspector.prototype._onloadsrc = function(e) {
  315.     const data = e.target.src;
  316.     const r = /^data:(image\/[-.+0-9a-zA-Z]+);base64,/.exec(data);
  317.  
  318.     if (r) {
  319.         const type = r[1];
  320.         const svg = type == 'image/svg+xml';
  321.  
  322.         const w = svg ? e.target.width : e.target.naturalWidth;
  323.         const h = svg ? e.target.height : e.target.naturalHeight;
  324.  
  325.         this._width = w;
  326.         this._height = h;
  327.         this._type = type;
  328.  
  329.         this._imageWidget.setAttribute('title', `${w}x${h}`);
  330.  
  331.         this.respondTo('inspectorValueChanged', this);
  332.     }
  333.     else
  334.         this._imageWidget.setAttribute('title', '');
  335. };
Test
  1. <?php $filetypes=array('image/jpeg', 'image/png', 'image/gif', 'image/svg+xml'); ?>
  2. <?php $maxsize=1000000; ?>
  3. <?php $width=320; ?>
  1. <?php $id=uniqid('id'); ?>
  1. .test_display img {
  2.     max-width: <?php echo $width; ?>px;
  3. }
  1. <div id="<?php echo $id; ?>" class="noprint">
  2. <div class="test_display"><img src="/files/images/htmlcssjs.jpg" alt=""/></div>
  3. </div>
  1. <?php head('javascript', '/objectivejs/Objective.js'); ?>
  2. <?php head('javascript', '/objectivejs/Responder.js'); ?>
  3. <?php head('javascript', '/objectivejs/View.js'); ?>
  4. <?php head('javascript', '/objectivejs/Inspector.js'); ?>
  5. <?php head('javascript', '/objectivejs/ImageInspector.js'); ?>
  6. <?php head('javascript', '/objectivejs/Validator.js'); ?>
  1. const filetypes = [<?php echo implode(',', array_map(function($s) { return "'$s'"; }, $filetypes)); ?>];
  2.  
  3. const inspector = new ImageInspector(null, { filetypes: filetypes, maxsize: <?php echo $maxsize; ?> });
  4.  
  5. const container = document.querySelector('#<?php echo $id; ?>');
  6.  
  7. inspector.setManagedWidget(container.querySelector('img'));
  1. inspector.reset();
SEE ALSO

Inspector, BooleanInspector, NumberInspector, StringInspector, SelectInspector, OptionInspector, SelectionInspector, ColorInspector, Validator

Comments

Your comment:
[p] [b] [i] [u] [s] [quote] [pre] [br] [code] [url] [email] strip help 2000

Enter a maximum of 2000 characters.
Improve the presentation of your text with the following formatting tags:
[p]paragraph[/p], [b]bold[/b], [i]italics[/i], [u]underline[/u], [s]strike[/s], [quote]citation[/quote], [pre]as is[/pre], [br]line break,
[url]http://www.izend.org[/url], [url=http://www.izend.org]site[/url], [email]izend@izend.org[/email], [email=izend@izend.org]izend[/email],
[code]command[/code], [code=language]source code in c, java, php, html, javascript, xml, css, sql, bash, dos, make, etc.[/code].