Arek Wtykło

Dodanie image2 (enchanged image dialog)

Showing 212 changed files with 10158 additions and 1 deletions
@@ -17,5 +17,5 @@ CKEDITOR.editorConfig = function( config ) { @@ -17,5 +17,5 @@ CKEDITOR.editorConfig = function( config ) {
17 ]; 17 ];
18 18
19 config.disableNativeSpellChecker = false; 19 config.disableNativeSpellChecker = false;
20 - config.extraPlugins = 'autolink'; 20 + config.extraPlugins = 'autolink,image2,widget,widgetselection,lineutils';
21 }; 21 };
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +
  6 +.cke_widget_wrapper:hover:after {
  7 + content: "id: " attr(data-cke-widget-id);
  8 + position: absolute;
  9 + top: 0;
  10 + right: 0;
  11 + padding: 2px 4px;
  12 + background: #EEE;
  13 + border: solid 1px #DDD;
  14 + border-radius: 2px;
  15 + color: #BBB;
  16 + font: bold 10px sans-serif;
  17 +}
  18 +
  19 +.align-left {
  20 + float: left;
  21 + margin-right: 20px;
  22 +}
  23 +
  24 +.align-right {
  25 + float: right;
  26 + margin-left: 20px;
  27 +}
  28 +
  29 +.align-center {
  30 + text-align: center;
  31 +}
  32 +
  33 +.align-center > figure {
  34 + display: inline-block;
  35 +}
  1 +<!DOCTYPE html>
  2 +<!--
  3 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  4 +For licensing, see LICENSE.md or http://ckeditor.com/license
  5 +-->
  6 +<html>
  7 +<head>
  8 + <meta charset="utf-8">
  9 + <title>Widget Image &mdash; CKEditor Sample</title>
  10 + <script src="../../../ckeditor.js"></script>
  11 + <script src="../../../dev/console/console.js"></script>
  12 + <script src="../../../dev/console/focusconsole.js"></script>
  13 + <script src="../../widget/dev/console.js"></script>
  14 + <script>
  15 + if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
  16 + CKEDITOR.tools.enableHtml5Elements( document );
  17 +
  18 + var editor;
  19 +
  20 + // The instanceReady event is fired, when an instance of CKEditor has finished
  21 + // its initialization.
  22 + CKEDITOR.on( 'instanceReady', function( ev ) {
  23 + editor = ev.editor;
  24 +
  25 + // Show this "on" button.
  26 + document.getElementById( 'readOnlyOn' ).style.display = '';
  27 +
  28 + // Event fired when the readOnly property changes.
  29 + editor.on( 'readOnly', function() {
  30 + document.getElementById( 'readOnlyOn' ).style.display = this.readOnly ? 'none' : '';
  31 + document.getElementById( 'readOnlyOff' ).style.display = this.readOnly ? '' : 'none';
  32 + });
  33 + });
  34 +
  35 + function toggleReadOnly( isReadOnly ) {
  36 + // Change the read-only state of the editor.
  37 + // http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setReadOnly
  38 + editor.setReadOnly( isReadOnly );
  39 + }
  40 +
  41 + </script>
  42 + <link href="../../../samples/old/sample.css" rel="stylesheet">
  43 +
  44 + <style>
  45 +
  46 + body {
  47 + font-size: 13px;
  48 + }
  49 + .editable {
  50 + padding: 20px;
  51 + border: 2px solid #dfdfdf;
  52 + overflow: auto;
  53 + }
  54 +
  55 + body p {
  56 + line-height: 1.8em;
  57 + }
  58 +
  59 + /* Reset some styles from sample.css */
  60 + .cke_editable.cke_editable_inline
  61 + {
  62 + cursor: auto;
  63 + }
  64 + .cke_editable.cke_editable_inline.cke_focus
  65 + {
  66 + box-shadow: none;
  67 + background: inherit;
  68 + cursor: auto;
  69 + }
  70 +
  71 + </style>
  72 + <link href="contents.css" rel="stylesheet">
  73 + <link href="../../../contents.css" rel="stylesheet">
  74 +</head>
  75 +<body>
  76 + <h1 class="samples">
  77 + <a href="../../../samples/old/index.html">CKEditor Samples</a> &raquo; Widget Image
  78 + </h1>
  79 +
  80 + <h2>Classic (iframe-based) Sample</h2>
  81 +
  82 + <textarea id="editor1" cols="10" rows="10">
  83 + <h1>Apollo 11</h1>
  84 +
  85 + <figure class="image" style="float: right">
  86 + <img alt="Saturn V" src="assets/image1.jpg" width="200" data-foo="*********" data-bar="@@@@@@@@" />
  87 + <figcaption>Roll out of Saturn V on launch pad</figcaption>
  88 + </figure>
  89 +
  90 + <p><strong>Apollo 11</strong> was the spaceflight that landed the first humans, Americans <a href="http://en.wikipedia.org/wiki/Neil_Armstrong" title="Neil Armstrong">Neil Armstrong</a> and <a href="http://en.wikipedia.org/wiki/Buzz_Aldrin" title="Buzz Aldrin">Buzz Aldrin</a>, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.</p>
  91 +
  92 + <p>Armstrong spent about <s>three and a half</s> two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&nbsp;kg) of lunar material for return to Earth. A third member of the mission, <a href="http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)" title="Michael Collins (astronaut)">Michael Collins</a>, piloted the <a href="http://en.wikipedia.org/wiki/Apollo_Command/Service_Module" title="Apollo Command/Service Module">command</a> spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.</p>
  93 +
  94 + <h2>Broadcasting and <em>quotes</em> <a id="quotes" name="quotes"></a></h2>
  95 +
  96 + <p>Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:</p>
  97 +
  98 + <blockquote>
  99 + <p>One small step for [a] man, one giant leap for mankind.</p>
  100 + </blockquote>
  101 +
  102 + <p>Apollo 11 effectively ended the <a href="http://en.wikipedia.org/wiki/Space_Race" title="Space Race">Space Race</a> and fulfilled a national goal proposed in 1961 by the late U.S. President <a href="http://en.wikipedia.org/wiki/John_F._Kennedy" title="John F. Kennedy">John F. Kennedy</a> in a speech before the United States Congress:</p>
  103 +
  104 + <blockquote>
  105 + <p>[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.</p>
  106 + </blockquote>
  107 +
  108 + <figure class="image" style="float: right">
  109 + <img alt="The Eagle" src="assets/image2.jpg" style="width: 200px" />
  110 + <figcaption>The Eagle in lunar orbit</figcaption>
  111 + </figure>
  112 +
  113 + <h2>Technical details <a id="tech-details" name="tech-details"></a></h2>
  114 +
  115 + <p>Launched by a <strong>Saturn V</strong> rocket from <a href="http://en.wikipedia.org/wiki/Kennedy_Space_Center" title="Kennedy Space Center">Kennedy Space Center</a> in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of <a href="http://en.wikipedia.org/wiki/NASA" title="NASA">NASA</a>&#39;s Apollo program. The Apollo spacecraft had three parts:</p>
  116 +
  117 + <ol>
  118 + <li><strong>Command Module</strong> with a cabin for the three astronauts which was the only part which landed back on Earth</li>
  119 + <li><strong>Service Module</strong> which supported the Command Module with propulsion, electrical power, oxygen and water</li>
  120 + <li><strong>Lunar Module</strong> for landing on the Moon.</li>
  121 + </ol>
  122 +
  123 + <p>After being sent to the Moon by the Saturn V&#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the <a href="http://en.wikipedia.org/wiki/Mare_Tranquillitatis" title="Mare Tranquillitatis">Sea of Tranquility</a>. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the <a href="http://en.wikipedia.org/wiki/Pacific_Ocean" title="Pacific Ocean">Pacific Ocean</a> on July 24.</p>
  124 +
  125 + <p style="text-align:center">
  126 + <img alt="Saturn V" src="assets/image1.jpg" width="200" />
  127 + </p>
  128 +
  129 + <hr />
  130 + <p style="text-align:right"><small>Source: <a href="http://en.wikipedia.org/wiki/Apollo_11">Wikipedia.org</a></small></p>
  131 + </textarea>
  132 +
  133 + <h2>Inline Sample</h2>
  134 +
  135 + <div id="editor2" contenteditable="true" class="editable">
  136 + <h2>Apollo 11</h2>
  137 +
  138 + <figure class="image" style="float: right">
  139 + <img alt="Saturn V" src="assets/image1.jpg" width="200" />
  140 + <figcaption>Roll out of Saturn V on launch pad</figcaption>
  141 + </figure>
  142 +
  143 + <p><strong>Apollo 11</strong> was the spaceflight that landed the first humans, Americans <a href="http://en.wikipedia.org/wiki/Neil_Armstrong" title="Neil Armstrong">Neil Armstrong</a> and <a href="http://en.wikipedia.org/wiki/Buzz_Aldrin" title="Buzz Aldrin">Buzz Aldrin</a>, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.</p>
  144 +
  145 + <p>Armstrong spent about <s>three and a half</s> two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&nbsp;kg) of lunar material for return to Earth. A third member of the mission, <a href="http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)" title="Michael Collins (astronaut)">Michael Collins</a>, piloted the <a href="http://en.wikipedia.org/wiki/Apollo_Command/Service_Module" title="Apollo Command/Service Module">command</a> spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.</p>
  146 +
  147 + <h2>Broadcasting and <em>quotes</em> <a id="quotes" name="quotes"></a></h2>
  148 +
  149 + <p>Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:</p>
  150 +
  151 + <blockquote>
  152 + <p>One small step for [a] man, one giant leap for mankind.</p>
  153 + </blockquote>
  154 +
  155 + <p>Apollo 11 effectively ended the <a href="http://en.wikipedia.org/wiki/Space_Race" title="Space Race">Space Race</a> and fulfilled a national goal proposed in 1961 by the late U.S. President <a href="http://en.wikipedia.org/wiki/John_F._Kennedy" title="John F. Kennedy">John F. Kennedy</a> in a speech before the United States Congress:</p>
  156 +
  157 + <blockquote>
  158 + <p>[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.</p>
  159 + </blockquote>
  160 +
  161 + <figure class="image" style="float: right">
  162 + <img alt="The Eagle" src="assets/image2.jpg" style="width: 200px" />
  163 + <figcaption>The Eagle in lunar orbit</figcaption>
  164 + </figure>
  165 +
  166 + <h2>Technical details <a id="tech-details" name="tech-details"></a></h2>
  167 +
  168 + <p>Launched by a <strong>Saturn V</strong> rocket from <a href="http://en.wikipedia.org/wiki/Kennedy_Space_Center" title="Kennedy Space Center">Kennedy Space Center</a> in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of <a href="http://en.wikipedia.org/wiki/NASA" title="NASA">NASA</a>&#39;s Apollo program. The Apollo spacecraft had three parts:</p>
  169 +
  170 + <ol>
  171 + <li><strong>Command Module</strong> with a cabin for the three astronauts which was the only part which landed back on Earth</li>
  172 + <li><strong>Service Module</strong> which supported the Command Module with propulsion, electrical power, oxygen and water</li>
  173 + <li><strong>Lunar Module</strong> for landing on the Moon.</li>
  174 + </ol>
  175 +
  176 + <p>After being sent to the Moon by the Saturn V&#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the <a href="http://en.wikipedia.org/wiki/Mare_Tranquillitatis" title="Mare Tranquillitatis">Sea of Tranquility</a>. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the <a href="http://en.wikipedia.org/wiki/Pacific_Ocean" title="Pacific Ocean">Pacific Ocean</a> on July 24.</p>
  177 +
  178 + <p style="text-align:center">
  179 + <img alt="Saturn V" src="assets/image1.jpg" width="200" />
  180 + </p>
  181 +
  182 + <hr />
  183 + <p style="text-align:right"><small>Source: <a href="http://en.wikipedia.org/wiki/Apollo_11">Wikipedia.org</a></small></p>
  184 + </div>
  185 +
  186 + <h2>Div Editing Area Sample</h2>
  187 +
  188 + <textarea id="editor3" cols="10" rows="10">
  189 + <h1>Apollo 11</h1>
  190 +
  191 + <figure class="caption" style="float: right">
  192 + <img alt="Saturn V" src="assets/image1.jpg" width="200" />
  193 + <figcaption>Roll out of Saturn V on launch pad</figcaption>
  194 + </figure>
  195 +
  196 + <p><strong>Apollo 11</strong> was the spaceflight that landed the first humans, Americans <a href="http://en.wikipedia.org/wiki/Neil_Armstrong" title="Neil Armstrong">Neil Armstrong</a> and <a href="http://en.wikipedia.org/wiki/Buzz_Aldrin" title="Buzz Aldrin">Buzz Aldrin</a>, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.</p>
  197 +
  198 + <p>Armstrong spent about <s>three and a half</s> two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&nbsp;kg) of lunar material for return to Earth. A third member of the mission, <a href="http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)" title="Michael Collins (astronaut)">Michael Collins</a>, piloted the <a href="http://en.wikipedia.org/wiki/Apollo_Command/Service_Module" title="Apollo Command/Service Module">command</a> spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.</p>
  199 +
  200 + <h2>Broadcasting and <em>quotes</em> <a id="quotes" name="quotes"></a></h2>
  201 +
  202 + <p>Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:</p>
  203 +
  204 + <blockquote>
  205 + <p>One small step for [a] man, one giant leap for mankind.</p>
  206 + </blockquote>
  207 +
  208 + <p>Apollo 11 effectively ended the <a href="http://en.wikipedia.org/wiki/Space_Race" title="Space Race">Space Race</a> and fulfilled a national goal proposed in 1961 by the late U.S. President <a href="http://en.wikipedia.org/wiki/John_F._Kennedy" title="John F. Kennedy">John F. Kennedy</a> in a speech before the United States Congress:</p>
  209 +
  210 + <blockquote>
  211 + <p>[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.</p>
  212 + </blockquote>
  213 +
  214 + <figure class="caption" style="float: right">
  215 + <img alt="The Eagle" src="assets/image2.jpg" style="width: 200px" />
  216 + <figcaption>The Eagle in lunar orbit</figcaption>
  217 + </figure>
  218 +
  219 + <h2>Technical Details <a id="tech-details" name="tech-details"></a></h2>
  220 +
  221 + <p>Launched by a <strong>Saturn V</strong> rocket from <a href="http://en.wikipedia.org/wiki/Kennedy_Space_Center" title="Kennedy Space Center">Kennedy Space Center</a> in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of <a href="http://en.wikipedia.org/wiki/NASA" title="NASA">NASA</a>&#39;s Apollo program. The Apollo spacecraft had three parts:</p>
  222 +
  223 + <ol>
  224 + <li><strong>Command Module</strong> with a cabin for the three astronauts which was the only part which landed back on Earth</li>
  225 + <li><strong>Service Module</strong> which supported the Command Module with propulsion, electrical power, oxygen and water</li>
  226 + <li><strong>Lunar Module</strong> for landing on the Moon.</li>
  227 + </ol>
  228 +
  229 + <p>After being sent to the Moon by the Saturn V&#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the <a href="http://en.wikipedia.org/wiki/Mare_Tranquillitatis" title="Mare Tranquillitatis">Sea of Tranquility</a>. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the <a href="http://en.wikipedia.org/wiki/Pacific_Ocean" title="Pacific Ocean">Pacific Ocean</a> on July 24.</p>
  230 +
  231 + <p style="text-align:center">
  232 + <img alt="Saturn V" src="assets/image1.jpg" width="200" />
  233 + </p>
  234 +
  235 + <hr />
  236 + <p style="text-align:right"><small>Source: <a href="http://en.wikipedia.org/wiki/Apollo_11">Wikipedia.org</a></small></p>
  237 + </textarea>
  238 +
  239 + <h2>alignClasses samples</h2>
  240 +
  241 + <textarea id="editor4" cols="10" rows="10">
  242 + <h1>Apollo 11</h1>
  243 +
  244 + <figure class="align-left image">
  245 + <img alt="Saturn V" src="assets/image1.jpg" width="200" data-foo="*********" data-bar="@@@@@@@@" />
  246 + <figcaption>Roll out of Saturn V on launch pad</figcaption>
  247 + </figure>
  248 +
  249 + <p><strong>Apollo 11</strong> was the spaceflight that landed the first humans, Americans <a href="http://en.wikipedia.org/wiki/Neil_Armstrong" title="Neil Armstrong">Neil Armstrong</a> and <a href="http://en.wikipedia.org/wiki/Buzz_Aldrin" title="Buzz Aldrin">Buzz Aldrin</a>, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.</p>
  250 +
  251 + <blockquote>
  252 + <p>[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.</p>
  253 + </blockquote>
  254 +
  255 + <figure class="align-right image">
  256 + <img alt="The Eagle" src="assets/image2.jpg" style="width: 200px" />
  257 + <figcaption>The Eagle in lunar orbit</figcaption>
  258 + </figure>
  259 +
  260 + <h2>Technical details <a id="tech-details" name="tech-details"></a></h2>
  261 +
  262 + <p>Launched by a <strong>Saturn V</strong> rocket from <a href="http://en.wikipedia.org/wiki/Kennedy_Space_Center" title="Kennedy Space Center">Kennedy Space Center</a> in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of <a href="http://en.wikipedia.org/wiki/NASA" title="NASA">NASA</a>&#39;s Apollo program. The Apollo spacecraft had three parts:</p>
  263 +
  264 + <ol>
  265 + <li><strong>Command Module</strong> with a cabin for the three astronauts which was the only part which landed back on Earth</li>
  266 + <li><strong>Service Module</strong> which supported the Command Module with propulsion, electrical power, oxygen and water</li>
  267 + <li><strong>Lunar Module</strong> for landing on the Moon.</li>
  268 + </ol>
  269 +
  270 + <p>After being sent to the Moon by the Saturn V&#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the <a href="http://en.wikipedia.org/wiki/Mare_Tranquillitatis" title="Mare Tranquillitatis">Sea of Tranquility</a>. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the <a href="http://en.wikipedia.org/wiki/Pacific_Ocean" title="Pacific Ocean">Pacific Ocean</a> on July 24.</p>
  271 +
  272 + <p class="align-center">
  273 + <img alt="Saturn V" src="assets/image1.jpg" width="200" />
  274 + </p>
  275 +
  276 + <hr />
  277 + <p style="text-align:right"><small>Source: <a href="http://en.wikipedia.org/wiki/Apollo_11">Wikipedia.org</a></small></p>
  278 + </textarea>
  279 +
  280 + <p>
  281 + <input id="readOnlyOn" onclick="toggleReadOnly( true );" type="button" value="Make it read-only" style="display:none">
  282 + <input id="readOnlyOff" onclick="toggleReadOnly( false );" type="button" value="Make it editable again" style="display:none">
  283 + </p>
  284 +
  285 + <script>
  286 +
  287 + CKEDITOR.disableAutoInline = true;
  288 +
  289 + CKEDITOR.replace( 'editor1', {
  290 + extraPlugins: 'image2',
  291 + height: 600,
  292 + contentsCss: [ '../../../contents.css', 'contents.css' ],
  293 + extraAllowedContent: 'img[data-foo,data-bar]',
  294 +
  295 + filebrowserBrowseUrl: '/ckfinder/ckfinder.html',
  296 + filebrowserImageBrowseUrl: '/ckfinder/ckfinder.html?Type=Images',
  297 + filebrowserUploadUrl: '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Files',
  298 + filebrowserImageUploadUrl: '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Images',
  299 + } );
  300 +
  301 + CKEDITOR.inline( 'editor2', {
  302 + extraPlugins: 'image2,sourcedialog'
  303 + } );
  304 +
  305 + CKEDITOR.replace( 'editor3', {
  306 + extraPlugins: 'image2,divarea',
  307 + height: 600
  308 + } );
  309 +
  310 + CKEDITOR.replace( 'editor4', {
  311 + extraPlugins: 'image2',
  312 + image2_alignClasses: [ 'align-left', 'align-center', 'align-right' ],
  313 + contentsCss: [ '../../../contents.css', 'contents.css' ],
  314 + height: 600
  315 + } );
  316 +
  317 + CKCONSOLE.create( 'widget', { editor: 'editor1' } );
  318 + CKCONSOLE.create( 'focus', { editor: 'editor1' } );
  319 + CKCONSOLE.create( 'widget', { editor: 'editor2', folded: true } );
  320 + CKCONSOLE.create( 'focus', { editor: 'editor2', folded: true } );
  321 + CKCONSOLE.create( 'widget', { editor: 'editor3' } );
  322 + CKCONSOLE.create( 'focus', { editor: 'editor3' } );
  323 + CKCONSOLE.create( 'widget', { editor: 'editor4' } );
  324 + CKCONSOLE.create( 'focus', { editor: 'editor4' } );
  325 +
  326 + </script>
  327 +
  328 + <div id="footer">
  329 + <hr>
  330 + <p>
  331 + CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
  332 + </p>
  333 + <p id="copy">
  334 + Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
  335 + Knabben. All rights reserved.
  336 + </p>
  337 + </div>
  338 +</body>
  339 +</html>
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +
  6 +/**
  7 + * @fileOverview Image plugin based on Widgets API
  8 + */
  9 +
  10 +'use strict';
  11 +
  12 +CKEDITOR.dialog.add( 'image2', function( editor ) {
  13 +
  14 + // RegExp: 123, 123px, empty string ""
  15 + var regexGetSizeOrEmpty = /(^\s*(\d+)(px)?\s*$)|^$/i,
  16 +
  17 + lockButtonId = CKEDITOR.tools.getNextId(),
  18 + resetButtonId = CKEDITOR.tools.getNextId(),
  19 +
  20 + lang = editor.lang.image2,
  21 + commonLang = editor.lang.common,
  22 +
  23 + lockResetStyle = 'margin-top:18px;width:40px;height:20px;',
  24 + lockResetHtml = new CKEDITOR.template(
  25 + '<div>' +
  26 + '<a href="javascript:void(0)" tabindex="-1" title="' + lang.lockRatio + '" class="cke_btn_locked" id="{lockButtonId}" role="checkbox">' +
  27 + '<span class="cke_icon"></span>' +
  28 + '<span class="cke_label">' + lang.lockRatio + '</span>' +
  29 + '</a>' +
  30 +
  31 + '<a href="javascript:void(0)" tabindex="-1" title="' + lang.resetSize + '" class="cke_btn_reset" id="{resetButtonId}" role="button">' +
  32 + '<span class="cke_label">' + lang.resetSize + '</span>' +
  33 + '</a>' +
  34 + '</div>' ).output( {
  35 + lockButtonId: lockButtonId,
  36 + resetButtonId: resetButtonId
  37 + } ),
  38 +
  39 + helpers = CKEDITOR.plugins.image2,
  40 +
  41 + // Editor instance configuration.
  42 + config = editor.config,
  43 +
  44 + hasFileBrowser = !!( config.filebrowserImageBrowseUrl || config.filebrowserBrowseUrl ),
  45 +
  46 + // Content restrictions defined by the widget which
  47 + // impact on dialog structure and presence of fields.
  48 + features = editor.widgets.registered.image.features,
  49 +
  50 + // Functions inherited from image2 plugin.
  51 + getNatural = helpers.getNatural,
  52 +
  53 + // Global variables referring to the dialog's context.
  54 + doc, widget, image,
  55 +
  56 + // Global variable referring to this dialog's image pre-loader.
  57 + preLoader,
  58 +
  59 + // Global variables holding the original size of the image.
  60 + domWidth, domHeight,
  61 +
  62 + // Global variables related to image pre-loading.
  63 + preLoadedWidth, preLoadedHeight, srcChanged,
  64 +
  65 + // Global variables related to size locking.
  66 + lockRatio, userDefinedLock,
  67 +
  68 + // Global variables referring to dialog fields and elements.
  69 + lockButton, resetButton, widthField, heightField,
  70 +
  71 + natural;
  72 +
  73 + // Validates dimension. Allowed values are:
  74 + // "123px", "123", "" (empty string)
  75 + function validateDimension() {
  76 + var match = this.getValue().match( regexGetSizeOrEmpty ),
  77 + isValid = !!( match && parseInt( match[ 1 ], 10 ) !== 0 );
  78 +
  79 + if ( !isValid )
  80 + alert( commonLang[ 'invalid' + CKEDITOR.tools.capitalize( this.id ) ] ); // jshint ignore:line
  81 +
  82 + return isValid;
  83 + }
  84 +
  85 + // Creates a function that pre-loads images. The callback function passes
  86 + // [image, width, height] or null if loading failed.
  87 + //
  88 + // @returns {Function}
  89 + function createPreLoader() {
  90 + var image = doc.createElement( 'img' ),
  91 + listeners = [];
  92 +
  93 + function addListener( event, callback ) {
  94 + listeners.push( image.once( event, function( evt ) {
  95 + removeListeners();
  96 + callback( evt );
  97 + } ) );
  98 + }
  99 +
  100 + function removeListeners() {
  101 + var l;
  102 +
  103 + while ( ( l = listeners.pop() ) )
  104 + l.removeListener();
  105 + }
  106 +
  107 + // @param {String} src.
  108 + // @param {Function} callback.
  109 + return function( src, callback, scope ) {
  110 + addListener( 'load', function() {
  111 + // Don't use image.$.(width|height) since it's buggy in IE9-10 (http://dev.ckeditor.com/ticket/11159)
  112 + var dimensions = getNatural( image );
  113 +
  114 + callback.call( scope, image, dimensions.width, dimensions.height );
  115 + } );
  116 +
  117 + addListener( 'error', function() {
  118 + callback( null );
  119 + } );
  120 +
  121 + addListener( 'abort', function() {
  122 + callback( null );
  123 + } );
  124 +
  125 + image.setAttribute( 'src',
  126 + ( config.baseHref || '' ) + src + '?' + Math.random().toString( 16 ).substring( 2 ) );
  127 + };
  128 + }
  129 +
  130 + // This function updates width and height fields once the
  131 + // "src" field is altered. Along with dimensions, also the
  132 + // dimensions lock is adjusted.
  133 + function onChangeSrc() {
  134 + var value = this.getValue();
  135 +
  136 + toggleDimensions( false );
  137 +
  138 + // Remember that src is different than default.
  139 + if ( value !== widget.data.src ) {
  140 + // Update dimensions of the image once it's preloaded.
  141 + preLoader( value, function( image, width, height ) {
  142 + // Re-enable width and height fields.
  143 + toggleDimensions( true );
  144 +
  145 + // There was problem loading the image. Unlock ratio.
  146 + if ( !image )
  147 + return toggleLockRatio( false );
  148 +
  149 + // Fill width field with the width of the new image.
  150 + widthField.setValue( editor.config.image2_prefillDimensions === false ? 0 : width );
  151 +
  152 + // Fill height field with the height of the new image.
  153 + heightField.setValue( editor.config.image2_prefillDimensions === false ? 0 : height );
  154 +
  155 + // Cache the new width.
  156 + preLoadedWidth = width;
  157 +
  158 + // Cache the new height.
  159 + preLoadedHeight = height;
  160 +
  161 + // Check for new lock value if image exist.
  162 + toggleLockRatio( helpers.checkHasNaturalRatio( image ) );
  163 + } );
  164 +
  165 + srcChanged = true;
  166 + }
  167 +
  168 + // Value is the same as in widget data but is was
  169 + // modified back in time. Roll back dimensions when restoring
  170 + // default src.
  171 + else if ( srcChanged ) {
  172 + // Re-enable width and height fields.
  173 + toggleDimensions( true );
  174 +
  175 + // Restore width field with cached width.
  176 + widthField.setValue( domWidth );
  177 +
  178 + // Restore height field with cached height.
  179 + heightField.setValue( domHeight );
  180 +
  181 + // Src equals default one back again.
  182 + srcChanged = false;
  183 + }
  184 +
  185 + // Value is the same as in widget data and it hadn't
  186 + // been modified.
  187 + else {
  188 + // Re-enable width and height fields.
  189 + toggleDimensions( true );
  190 + }
  191 + }
  192 +
  193 + function onChangeDimension() {
  194 + // If ratio is un-locked, then we don't care what's next.
  195 + if ( !lockRatio )
  196 + return;
  197 +
  198 + var value = this.getValue();
  199 +
  200 + // No reason to auto-scale or unlock if the field is empty.
  201 + if ( !value )
  202 + return;
  203 +
  204 + // If the value of the field is invalid (e.g. with %), unlock ratio.
  205 + if ( !value.match( regexGetSizeOrEmpty ) )
  206 + toggleLockRatio( false );
  207 +
  208 + // No automatic re-scale when dimension is '0'.
  209 + if ( value === '0' )
  210 + return;
  211 +
  212 + var isWidth = this.id == 'width',
  213 + // If dialog opened for the new image, domWidth and domHeight
  214 + // will be empty. Use dimensions from pre-loader in such case instead.
  215 + width = domWidth || preLoadedWidth,
  216 + height = domHeight || preLoadedHeight;
  217 +
  218 + // If changing width, then auto-scale height.
  219 + if ( isWidth )
  220 + value = Math.round( height * ( value / width ) );
  221 +
  222 + // If changing height, then auto-scale width.
  223 + else
  224 + value = Math.round( width * ( value / height ) );
  225 +
  226 + // If the value is a number, apply it to the other field.
  227 + if ( !isNaN( value ) )
  228 + ( isWidth ? heightField : widthField ).setValue( value );
  229 + }
  230 +
  231 + // Set-up function for lock and reset buttons:
  232 + // * Adds lock and reset buttons to focusables. Check if button exist first
  233 + // because it may be disabled e.g. due to ACF restrictions.
  234 + // * Register mouseover and mouseout event listeners for UI manipulations.
  235 + // * Register click event listeners for buttons.
  236 + function onLoadLockReset() {
  237 + var dialog = this.getDialog();
  238 +
  239 + function setupMouseClasses( el ) {
  240 + el.on( 'mouseover', function() {
  241 + this.addClass( 'cke_btn_over' );
  242 + }, el );
  243 +
  244 + el.on( 'mouseout', function() {
  245 + this.removeClass( 'cke_btn_over' );
  246 + }, el );
  247 + }
  248 +
  249 + // Create references to lock and reset buttons for this dialog instance.
  250 + lockButton = doc.getById( lockButtonId );
  251 + resetButton = doc.getById( resetButtonId );
  252 +
  253 + // Activate (Un)LockRatio button
  254 + if ( lockButton ) {
  255 + // Consider that there's an additional focusable field
  256 + // in the dialog when the "browse" button is visible.
  257 + dialog.addFocusable( lockButton, 4 + hasFileBrowser );
  258 +
  259 + lockButton.on( 'click', function( evt ) {
  260 + toggleLockRatio();
  261 + evt.data && evt.data.preventDefault();
  262 + }, this.getDialog() );
  263 +
  264 + setupMouseClasses( lockButton );
  265 + }
  266 +
  267 + // Activate the reset size button.
  268 + if ( resetButton ) {
  269 + // Consider that there's an additional focusable field
  270 + // in the dialog when the "browse" button is visible.
  271 + dialog.addFocusable( resetButton, 5 + hasFileBrowser );
  272 +
  273 + // Fills width and height fields with the original dimensions of the
  274 + // image (stored in widget#data since widget#init).
  275 + resetButton.on( 'click', function( evt ) {
  276 + // If there's a new image loaded, reset button should revert
  277 + // cached dimensions of pre-loaded DOM element.
  278 + if ( srcChanged ) {
  279 + widthField.setValue( preLoadedWidth );
  280 + heightField.setValue( preLoadedHeight );
  281 + }
  282 +
  283 + // If the old image remains, reset button should revert
  284 + // dimensions as loaded when the dialog was first shown.
  285 + else {
  286 + widthField.setValue( domWidth );
  287 + heightField.setValue( domHeight );
  288 + }
  289 +
  290 + evt.data && evt.data.preventDefault();
  291 + }, this );
  292 +
  293 + setupMouseClasses( resetButton );
  294 + }
  295 + }
  296 +
  297 + function toggleLockRatio( enable ) {
  298 + // No locking if there's no radio (i.e. due to ACF).
  299 + if ( !lockButton )
  300 + return;
  301 +
  302 + if ( typeof enable == 'boolean' ) {
  303 + // If user explicitly wants to decide whether
  304 + // to lock or not, don't do anything.
  305 + if ( userDefinedLock )
  306 + return;
  307 +
  308 + lockRatio = enable;
  309 + }
  310 +
  311 + // Undefined. User changed lock value.
  312 + else {
  313 + var width = widthField.getValue(),
  314 + height;
  315 +
  316 + userDefinedLock = true;
  317 + lockRatio = !lockRatio;
  318 +
  319 + // Automatically adjust height to width to match
  320 + // the original ratio (based on dom- dimensions).
  321 + if ( lockRatio && width ) {
  322 + height = domHeight / domWidth * width;
  323 +
  324 + if ( !isNaN( height ) )
  325 + heightField.setValue( Math.round( height ) );
  326 + }
  327 + }
  328 +
  329 + lockButton[ lockRatio ? 'removeClass' : 'addClass' ]( 'cke_btn_unlocked' );
  330 + lockButton.setAttribute( 'aria-checked', lockRatio );
  331 +
  332 + // Ratio button hc presentation - WHITE SQUARE / BLACK SQUARE
  333 + if ( CKEDITOR.env.hc ) {
  334 + var icon = lockButton.getChild( 0 );
  335 + icon.setHtml( lockRatio ? CKEDITOR.env.ie ? '\u25A0' : '\u25A3' : CKEDITOR.env.ie ? '\u25A1' : '\u25A2' );
  336 + }
  337 + }
  338 +
  339 + function toggleDimensions( enable ) {
  340 + var method = enable ? 'enable' : 'disable';
  341 +
  342 + widthField[ method ]();
  343 + heightField[ method ]();
  344 + }
  345 +
  346 + var srcBoxChildren = [
  347 + {
  348 + id: 'src',
  349 + type: 'text',
  350 + label: commonLang.url,
  351 + onKeyup: onChangeSrc,
  352 + onChange: onChangeSrc,
  353 + setup: function( widget ) {
  354 + this.setValue( widget.data.src );
  355 + },
  356 + commit: function( widget ) {
  357 + widget.setData( 'src', this.getValue() );
  358 + },
  359 + validate: CKEDITOR.dialog.validate.notEmpty( lang.urlMissing )
  360 + }
  361 + ];
  362 +
  363 + // Render the "Browse" button on demand to avoid an "empty" (hidden child)
  364 + // space in dialog layout that distorts the UI.
  365 + if ( hasFileBrowser ) {
  366 + srcBoxChildren.push( {
  367 + type: 'button',
  368 + id: 'browse',
  369 + // v-align with the 'txtUrl' field.
  370 + // TODO: We need something better than a fixed size here.
  371 + style: 'display:inline-block;margin-top:14px;',
  372 + align: 'center',
  373 + label: editor.lang.common.browseServer,
  374 + hidden: true,
  375 + filebrowser: 'info:src'
  376 + } );
  377 + }
  378 +
  379 + return {
  380 + title: lang.title,
  381 + minWidth: 250,
  382 + minHeight: 100,
  383 + onLoad: function() {
  384 + // Create a "global" reference to the document for this dialog instance.
  385 + doc = this._.element.getDocument();
  386 +
  387 + // Create a pre-loader used for determining dimensions of new images.
  388 + preLoader = createPreLoader();
  389 + },
  390 + onShow: function() {
  391 + // Create a "global" reference to edited widget.
  392 + widget = this.widget;
  393 +
  394 + // Create a "global" reference to widget's image.
  395 + image = widget.parts.image;
  396 +
  397 + // Reset global variables.
  398 + srcChanged = userDefinedLock = lockRatio = false;
  399 +
  400 + // Natural dimensions of the image.
  401 + natural = getNatural( image );
  402 +
  403 + // Get the natural width of the image.
  404 + preLoadedWidth = domWidth = natural.width;
  405 +
  406 + // Get the natural height of the image.
  407 + preLoadedHeight = domHeight = natural.height;
  408 + },
  409 + contents: [
  410 + {
  411 + id: 'info',
  412 + label: lang.infoTab,
  413 + elements: [
  414 + {
  415 + type: 'vbox',
  416 + padding: 0,
  417 + children: [
  418 + {
  419 + type: 'hbox',
  420 + widths: [ '100%' ],
  421 + className: 'cke_dialog_image_url',
  422 + children: srcBoxChildren
  423 + }
  424 + ]
  425 + },
  426 + {
  427 + id: 'alt',
  428 + type: 'text',
  429 + label: lang.alt,
  430 + setup: function( widget ) {
  431 + this.setValue( widget.data.alt );
  432 + },
  433 + commit: function( widget ) {
  434 + widget.setData( 'alt', this.getValue() );
  435 + },
  436 + validate: editor.config.image2_altRequired === true ? CKEDITOR.dialog.validate.notEmpty( lang.altMissing ) : null
  437 + },
  438 + {
  439 + type: 'hbox',
  440 + widths: [ '25%', '25%', '50%' ],
  441 + requiredContent: features.dimension.requiredContent,
  442 + children: [
  443 + {
  444 + type: 'text',
  445 + width: '45px',
  446 + id: 'width',
  447 + label: commonLang.width,
  448 + validate: validateDimension,
  449 + onKeyUp: onChangeDimension,
  450 + onLoad: function() {
  451 + widthField = this;
  452 + },
  453 + setup: function( widget ) {
  454 + this.setValue( widget.data.width );
  455 + },
  456 + commit: function( widget ) {
  457 + widget.setData( 'width', this.getValue() );
  458 + }
  459 + },
  460 + {
  461 + type: 'text',
  462 + id: 'height',
  463 + width: '45px',
  464 + label: commonLang.height,
  465 + validate: validateDimension,
  466 + onKeyUp: onChangeDimension,
  467 + onLoad: function() {
  468 + heightField = this;
  469 + },
  470 + setup: function( widget ) {
  471 + this.setValue( widget.data.height );
  472 + },
  473 + commit: function( widget ) {
  474 + widget.setData( 'height', this.getValue() );
  475 + }
  476 + },
  477 + {
  478 + id: 'lock',
  479 + type: 'html',
  480 + style: lockResetStyle,
  481 + onLoad: onLoadLockReset,
  482 + setup: function( widget ) {
  483 + toggleLockRatio( widget.data.lock );
  484 + },
  485 + commit: function( widget ) {
  486 + widget.setData( 'lock', lockRatio );
  487 + },
  488 + html: lockResetHtml
  489 + }
  490 + ]
  491 + },
  492 + {
  493 + type: 'hbox',
  494 + id: 'alignment',
  495 + requiredContent: features.align.requiredContent,
  496 + children: [
  497 + {
  498 + id: 'align',
  499 + type: 'radio',
  500 + items: [
  501 + [ commonLang.alignNone, 'none' ],
  502 + [ commonLang.alignLeft, 'left' ],
  503 + [ commonLang.alignCenter, 'center' ],
  504 + [ commonLang.alignRight, 'right' ]
  505 + ],
  506 + label: commonLang.align,
  507 + setup: function( widget ) {
  508 + this.setValue( widget.data.align );
  509 + },
  510 + commit: function( widget ) {
  511 + widget.setData( 'align', this.getValue() );
  512 + }
  513 + }
  514 + ]
  515 + },
  516 + {
  517 + id: 'hasCaption',
  518 + type: 'checkbox',
  519 + label: lang.captioned,
  520 + requiredContent: features.caption.requiredContent,
  521 + setup: function( widget ) {
  522 + this.setValue( widget.data.hasCaption );
  523 + },
  524 + commit: function( widget ) {
  525 + widget.setData( 'hasCaption', this.getValue() );
  526 + }
  527 + }
  528 + ]
  529 + },
  530 + {
  531 + id: 'Upload',
  532 + hidden: true,
  533 + filebrowser: 'uploadButton',
  534 + label: lang.uploadTab,
  535 + elements: [
  536 + {
  537 + type: 'file',
  538 + id: 'upload',
  539 + label: lang.btnUpload,
  540 + style: 'height:40px'
  541 + },
  542 + {
  543 + type: 'fileButton',
  544 + id: 'uploadButton',
  545 + filebrowser: 'info:src',
  546 + label: lang.btnUpload,
  547 + 'for': [ 'Upload', 'upload' ]
  548 + }
  549 + ]
  550 + }
  551 + ]
  552 + };
  553 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +
  6 +/**
  7 + * @fileOverview Image plugin based on Widgets API
  8 + */
  9 +
  10 +'use strict';
  11 +
  12 +CKEDITOR.dialog.add( 'image2', function( editor ) {
  13 +
  14 + // RegExp: 123, 123px, empty string ""
  15 + var regexGetSizeOrEmpty = /(^\s*(\d+)(px)?\s*$)|^$/i,
  16 +
  17 + lockButtonId = CKEDITOR.tools.getNextId(),
  18 + resetButtonId = CKEDITOR.tools.getNextId(),
  19 +
  20 + lang = editor.lang.image2,
  21 + commonLang = editor.lang.common,
  22 +
  23 + lockResetStyle = 'margin-top:18px;width:40px;height:20px;',
  24 + lockResetHtml = new CKEDITOR.template(
  25 + '<div>' +
  26 + '<a href="javascript:void(0)" tabindex="-1" title="' + lang.lockRatio + '" class="cke_btn_locked" id="{lockButtonId}" role="checkbox">' +
  27 + '<span class="cke_icon"></span>' +
  28 + '<span class="cke_label">' + lang.lockRatio + '</span>' +
  29 + '</a>' +
  30 +
  31 + '<a href="javascript:void(0)" tabindex="-1" title="' + lang.resetSize + '" class="cke_btn_reset" id="{resetButtonId}" role="button">' +
  32 + '<span class="cke_label">' + lang.resetSize + '</span>' +
  33 + '</a>' +
  34 + '</div>' ).output( {
  35 + lockButtonId: lockButtonId,
  36 + resetButtonId: resetButtonId
  37 + } ),
  38 +
  39 + helpers = CKEDITOR.plugins.image2,
  40 +
  41 + // Editor instance configuration.
  42 + config = editor.config,
  43 +
  44 + hasFileBrowser = !!( config.filebrowserImageBrowseUrl || config.filebrowserBrowseUrl ),
  45 +
  46 + // Content restrictions defined by the widget which
  47 + // impact on dialog structure and presence of fields.
  48 + features = editor.widgets.registered.image.features,
  49 +
  50 + // Functions inherited from image2 plugin.
  51 + getNatural = helpers.getNatural,
  52 +
  53 + // Global variables referring to the dialog's context.
  54 + doc, widget, image,
  55 +
  56 + // Global variable referring to this dialog's image pre-loader.
  57 + preLoader,
  58 +
  59 + // Global variables holding the original size of the image.
  60 + domWidth, domHeight,
  61 +
  62 + // Global variables related to image pre-loading.
  63 + preLoadedWidth, preLoadedHeight, srcChanged,
  64 +
  65 + // Global variables related to size locking.
  66 + lockRatio, userDefinedLock,
  67 +
  68 + // Global variables referring to dialog fields and elements.
  69 + lockButton, resetButton, widthField, heightField,
  70 +
  71 + natural;
  72 +
  73 + // Validates dimension. Allowed values are:
  74 + // "123px", "123", "" (empty string)
  75 + function validateDimension() {
  76 + var match = this.getValue().match( regexGetSizeOrEmpty ),
  77 + isValid = !!( match && parseInt( match[ 1 ], 10 ) !== 0 );
  78 +
  79 + if ( !isValid )
  80 + alert( commonLang[ 'invalid' + CKEDITOR.tools.capitalize( this.id ) ] ); // jshint ignore:line
  81 +
  82 + return isValid;
  83 + }
  84 +
  85 + // Creates a function that pre-loads images. The callback function passes
  86 + // [image, width, height] or null if loading failed.
  87 + //
  88 + // @returns {Function}
  89 + function createPreLoader() {
  90 + var image = doc.createElement( 'img' ),
  91 + listeners = [];
  92 +
  93 + function addListener( event, callback ) {
  94 + listeners.push( image.once( event, function( evt ) {
  95 + removeListeners();
  96 + callback( evt );
  97 + } ) );
  98 + }
  99 +
  100 + function removeListeners() {
  101 + var l;
  102 +
  103 + while ( ( l = listeners.pop() ) )
  104 + l.removeListener();
  105 + }
  106 +
  107 + // @param {String} src.
  108 + // @param {Function} callback.
  109 + return function( src, callback, scope ) {
  110 + addListener( 'load', function() {
  111 + // Don't use image.$.(width|height) since it's buggy in IE9-10 (http://dev.ckeditor.com/ticket/11159)
  112 + var dimensions = getNatural( image );
  113 +
  114 + callback.call( scope, image, dimensions.width, dimensions.height );
  115 + } );
  116 +
  117 + addListener( 'error', function() {
  118 + callback( null );
  119 + } );
  120 +
  121 + addListener( 'abort', function() {
  122 + callback( null );
  123 + } );
  124 +
  125 + image.setAttribute( 'src',
  126 + ( config.baseHref || '' ) + src + '?' + Math.random().toString( 16 ).substring( 2 ) );
  127 + };
  128 + }
  129 +
  130 + // This function updates width and height fields once the
  131 + // "src" field is altered. Along with dimensions, also the
  132 + // dimensions lock is adjusted.
  133 + function onChangeSrc() {
  134 + var value = this.getValue();
  135 +
  136 + toggleDimensions( false );
  137 +
  138 + // Remember that src is different than default.
  139 + if ( value !== widget.data.src ) {
  140 + // Update dimensions of the image once it's preloaded.
  141 + preLoader( value, function( image, width, height ) {
  142 + // Re-enable width and height fields.
  143 + toggleDimensions( true );
  144 +
  145 + // There was problem loading the image. Unlock ratio.
  146 + if ( !image )
  147 + return toggleLockRatio( false );
  148 +
  149 + // Fill width field with the width of the new image.
  150 + widthField.setValue( editor.config.image2_prefillDimensions === false ? 0 : width );
  151 +
  152 + // Fill height field with the height of the new image.
  153 + heightField.setValue( editor.config.image2_prefillDimensions === false ? 0 : height );
  154 +
  155 + // Cache the new width.
  156 + preLoadedWidth = width;
  157 +
  158 + // Cache the new height.
  159 + preLoadedHeight = height;
  160 +
  161 + // Check for new lock value if image exist.
  162 + toggleLockRatio( helpers.checkHasNaturalRatio( image ) );
  163 + } );
  164 +
  165 + srcChanged = true;
  166 + }
  167 +
  168 + // Value is the same as in widget data but is was
  169 + // modified back in time. Roll back dimensions when restoring
  170 + // default src.
  171 + else if ( srcChanged ) {
  172 + // Re-enable width and height fields.
  173 + toggleDimensions( true );
  174 +
  175 + // Restore width field with cached width.
  176 + widthField.setValue( domWidth );
  177 +
  178 + // Restore height field with cached height.
  179 + heightField.setValue( domHeight );
  180 +
  181 + // Src equals default one back again.
  182 + srcChanged = false;
  183 + }
  184 +
  185 + // Value is the same as in widget data and it hadn't
  186 + // been modified.
  187 + else {
  188 + // Re-enable width and height fields.
  189 + toggleDimensions( true );
  190 + }
  191 + }
  192 +
  193 + function onChangeDimension() {
  194 + // If ratio is un-locked, then we don't care what's next.
  195 + if ( !lockRatio )
  196 + return;
  197 +
  198 + var value = this.getValue();
  199 +
  200 + // No reason to auto-scale or unlock if the field is empty.
  201 + if ( !value )
  202 + return;
  203 +
  204 + // If the value of the field is invalid (e.g. with %), unlock ratio.
  205 + if ( !value.match( regexGetSizeOrEmpty ) )
  206 + toggleLockRatio( false );
  207 +
  208 + // No automatic re-scale when dimension is '0'.
  209 + if ( value === '0' )
  210 + return;
  211 +
  212 + var isWidth = this.id == 'width',
  213 + // If dialog opened for the new image, domWidth and domHeight
  214 + // will be empty. Use dimensions from pre-loader in such case instead.
  215 + width = domWidth || preLoadedWidth,
  216 + height = domHeight || preLoadedHeight;
  217 +
  218 + // If changing width, then auto-scale height.
  219 + if ( isWidth )
  220 + value = Math.round( height * ( value / width ) );
  221 +
  222 + // If changing height, then auto-scale width.
  223 + else
  224 + value = Math.round( width * ( value / height ) );
  225 +
  226 + // If the value is a number, apply it to the other field.
  227 + if ( !isNaN( value ) )
  228 + ( isWidth ? heightField : widthField ).setValue( value );
  229 + }
  230 +
  231 + // Set-up function for lock and reset buttons:
  232 + // * Adds lock and reset buttons to focusables. Check if button exist first
  233 + // because it may be disabled e.g. due to ACF restrictions.
  234 + // * Register mouseover and mouseout event listeners for UI manipulations.
  235 + // * Register click event listeners for buttons.
  236 + function onLoadLockReset() {
  237 + var dialog = this.getDialog();
  238 +
  239 + function setupMouseClasses( el ) {
  240 + el.on( 'mouseover', function() {
  241 + this.addClass( 'cke_btn_over' );
  242 + }, el );
  243 +
  244 + el.on( 'mouseout', function() {
  245 + this.removeClass( 'cke_btn_over' );
  246 + }, el );
  247 + }
  248 +
  249 + // Create references to lock and reset buttons for this dialog instance.
  250 + lockButton = doc.getById( lockButtonId );
  251 + resetButton = doc.getById( resetButtonId );
  252 +
  253 + // Activate (Un)LockRatio button
  254 + if ( lockButton ) {
  255 + // Consider that there's an additional focusable field
  256 + // in the dialog when the "browse" button is visible.
  257 + dialog.addFocusable( lockButton, 4 + hasFileBrowser );
  258 +
  259 + lockButton.on( 'click', function( evt ) {
  260 + toggleLockRatio();
  261 + evt.data && evt.data.preventDefault();
  262 + }, this.getDialog() );
  263 +
  264 + setupMouseClasses( lockButton );
  265 + }
  266 +
  267 + // Activate the reset size button.
  268 + if ( resetButton ) {
  269 + // Consider that there's an additional focusable field
  270 + // in the dialog when the "browse" button is visible.
  271 + dialog.addFocusable( resetButton, 5 + hasFileBrowser );
  272 +
  273 + // Fills width and height fields with the original dimensions of the
  274 + // image (stored in widget#data since widget#init).
  275 + resetButton.on( 'click', function( evt ) {
  276 + // If there's a new image loaded, reset button should revert
  277 + // cached dimensions of pre-loaded DOM element.
  278 + if ( srcChanged ) {
  279 + widthField.setValue( preLoadedWidth );
  280 + heightField.setValue( preLoadedHeight );
  281 + }
  282 +
  283 + // If the old image remains, reset button should revert
  284 + // dimensions as loaded when the dialog was first shown.
  285 + else {
  286 + widthField.setValue( domWidth );
  287 + heightField.setValue( domHeight );
  288 + }
  289 +
  290 + evt.data && evt.data.preventDefault();
  291 + }, this );
  292 +
  293 + setupMouseClasses( resetButton );
  294 + }
  295 + }
  296 +
  297 + function toggleLockRatio( enable ) {
  298 + // No locking if there's no radio (i.e. due to ACF).
  299 + if ( !lockButton )
  300 + return;
  301 +
  302 + if ( typeof enable == 'boolean' ) {
  303 + // If user explicitly wants to decide whether
  304 + // to lock or not, don't do anything.
  305 + if ( userDefinedLock )
  306 + return;
  307 +
  308 + lockRatio = enable;
  309 + }
  310 +
  311 + // Undefined. User changed lock value.
  312 + else {
  313 + var width = widthField.getValue(),
  314 + height;
  315 +
  316 + userDefinedLock = true;
  317 + lockRatio = !lockRatio;
  318 +
  319 + // Automatically adjust height to width to match
  320 + // the original ratio (based on dom- dimensions).
  321 + if ( lockRatio && width ) {
  322 + height = domHeight / domWidth * width;
  323 +
  324 + if ( !isNaN( height ) )
  325 + heightField.setValue( Math.round( height ) );
  326 + }
  327 + }
  328 +
  329 + lockButton[ lockRatio ? 'removeClass' : 'addClass' ]( 'cke_btn_unlocked' );
  330 + lockButton.setAttribute( 'aria-checked', lockRatio );
  331 +
  332 + // Ratio button hc presentation - WHITE SQUARE / BLACK SQUARE
  333 + if ( CKEDITOR.env.hc ) {
  334 + var icon = lockButton.getChild( 0 );
  335 + icon.setHtml( lockRatio ? CKEDITOR.env.ie ? '\u25A0' : '\u25A3' : CKEDITOR.env.ie ? '\u25A1' : '\u25A2' );
  336 + }
  337 + }
  338 +
  339 + function toggleDimensions( enable ) {
  340 + var method = enable ? 'enable' : 'disable';
  341 +
  342 + widthField[ method ]();
  343 + heightField[ method ]();
  344 + }
  345 +
  346 + var srcBoxChildren = [
  347 + {
  348 + id: 'src',
  349 + type: 'text',
  350 + label: commonLang.url,
  351 + onKeyup: onChangeSrc,
  352 + onChange: onChangeSrc,
  353 + setup: function( widget ) {
  354 + this.setValue( widget.data.src );
  355 + },
  356 + commit: function( widget ) {
  357 + widget.setData( 'src', this.getValue() );
  358 + },
  359 + validate: CKEDITOR.dialog.validate.notEmpty( lang.urlMissing )
  360 + }
  361 + ];
  362 +
  363 + // Render the "Browse" button on demand to avoid an "empty" (hidden child)
  364 + // space in dialog layout that distorts the UI.
  365 + if ( hasFileBrowser ) {
  366 + srcBoxChildren.push( {
  367 + type: 'button',
  368 + id: 'browse',
  369 + // v-align with the 'txtUrl' field.
  370 + // TODO: We need something better than a fixed size here.
  371 + style: 'display:inline-block;margin-top:14px;',
  372 + align: 'center',
  373 + label: editor.lang.common.browseServer,
  374 + hidden: true,
  375 + filebrowser: 'info:src'
  376 + } );
  377 + }
  378 +
  379 + return {
  380 + title: lang.title,
  381 + minWidth: 250,
  382 + minHeight: 100,
  383 + onLoad: function() {
  384 + // Create a "global" reference to the document for this dialog instance.
  385 + doc = this._.element.getDocument();
  386 +
  387 + // Create a pre-loader used for determining dimensions of new images.
  388 + preLoader = createPreLoader();
  389 + },
  390 + onShow: function() {
  391 + // Create a "global" reference to edited widget.
  392 + widget = this.widget;
  393 +
  394 + // Create a "global" reference to widget's image.
  395 + image = widget.parts.image;
  396 +
  397 + // Reset global variables.
  398 + srcChanged = userDefinedLock = lockRatio = false;
  399 +
  400 + // Natural dimensions of the image.
  401 + natural = getNatural( image );
  402 +
  403 + // Get the natural width of the image.
  404 + preLoadedWidth = domWidth = natural.width;
  405 +
  406 + // Get the natural height of the image.
  407 + preLoadedHeight = domHeight = natural.height;
  408 + },
  409 + contents: [
  410 + {
  411 + id: 'info',
  412 + label: lang.infoTab,
  413 + elements: [
  414 + {
  415 + type: 'vbox',
  416 + padding: 0,
  417 + children: [
  418 + {
  419 + type: 'hbox',
  420 + widths: [ '100%' ],
  421 + className: 'cke_dialog_image_url',
  422 + children: srcBoxChildren
  423 + }
  424 + ]
  425 + },
  426 + {
  427 + id: 'alt',
  428 + type: 'text',
  429 + label: lang.alt,
  430 + setup: function( widget ) {
  431 + this.setValue( widget.data.alt );
  432 + },
  433 + commit: function( widget ) {
  434 + widget.setData( 'alt', this.getValue() );
  435 + },
  436 + validate: editor.config.image2_altRequired === true ? CKEDITOR.dialog.validate.notEmpty( lang.altMissing ) : null
  437 + },
  438 + {
  439 + type: 'hbox',
  440 + widths: [ '25%', '25%', '50%' ],
  441 + requiredContent: features.dimension.requiredContent,
  442 + children: [
  443 + {
  444 + type: 'text',
  445 + width: '45px',
  446 + id: 'width',
  447 + label: commonLang.width,
  448 + validate: validateDimension,
  449 + onKeyUp: onChangeDimension,
  450 + onLoad: function() {
  451 + widthField = this;
  452 + },
  453 + setup: function( widget ) {
  454 + this.setValue( widget.data.width );
  455 + },
  456 + commit: function( widget ) {
  457 + widget.setData( 'width', this.getValue() );
  458 + }
  459 + },
  460 + {
  461 + type: 'text',
  462 + id: 'height',
  463 + width: '45px',
  464 + label: commonLang.height,
  465 + validate: validateDimension,
  466 + onKeyUp: onChangeDimension,
  467 + onLoad: function() {
  468 + heightField = this;
  469 + },
  470 + setup: function( widget ) {
  471 + this.setValue( widget.data.height );
  472 + },
  473 + commit: function( widget ) {
  474 + widget.setData( 'height', this.getValue() );
  475 + }
  476 + },
  477 + {
  478 + id: 'lock',
  479 + type: 'html',
  480 + style: lockResetStyle,
  481 + onLoad: onLoadLockReset,
  482 + setup: function( widget ) {
  483 + toggleLockRatio( widget.data.lock );
  484 + },
  485 + commit: function( widget ) {
  486 + widget.setData( 'lock', lockRatio );
  487 + },
  488 + html: lockResetHtml
  489 + }
  490 + ]
  491 + },
  492 + {
  493 + type: 'hbox',
  494 + id: 'alignment',
  495 + requiredContent: features.align.requiredContent,
  496 + children: [
  497 + {
  498 + id: 'align',
  499 + type: 'radio',
  500 + items: [
  501 + [ commonLang.alignNone, 'none' ],
  502 + [ commonLang.alignLeft, 'left' ],
  503 + [ commonLang.alignCenter, 'center' ],
  504 + [ commonLang.alignRight, 'right' ]
  505 + ],
  506 + label: commonLang.align,
  507 + setup: function( widget ) {
  508 + this.setValue( widget.data.align );
  509 + },
  510 + commit: function( widget ) {
  511 + widget.setData( 'align', this.getValue() );
  512 + }
  513 + }
  514 + ]
  515 + },
  516 + {
  517 + id: 'hasCaption',
  518 + type: 'checkbox',
  519 + label: lang.captioned,
  520 + requiredContent: features.caption.requiredContent,
  521 + setup: function( widget ) {
  522 + this.setValue( widget.data.hasCaption );
  523 + },
  524 + commit: function( widget ) {
  525 + widget.setData( 'hasCaption', this.getValue() );
  526 + }
  527 + }
  528 + ]
  529 + },
  530 + {
  531 + id: 'Upload',
  532 + hidden: true,
  533 + filebrowser: 'uploadButton',
  534 + label: lang.uploadTab,
  535 + elements: [
  536 + {
  537 + type: 'file',
  538 + id: 'upload',
  539 + label: lang.btnUpload,
  540 + style: 'height:40px'
  541 + },
  542 + {
  543 + type: 'fileButton',
  544 + id: 'uploadButton',
  545 + filebrowser: 'info:src',
  546 + label: lang.btnUpload,
  547 + 'for': [ 'Upload', 'upload' ]
  548 + }
  549 + ]
  550 + }
  551 + ]
  552 + };
  553 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'af', {
  6 + alt: 'Alternatiewe teks',
  7 + btnUpload: 'Stuur na bediener',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Afbeelding informasie',
  11 + lockRatio: 'Vaste proporsie',
  12 + menu: 'Afbeelding eienskappe',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Herstel grootte',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Afbeelding eienskappe',
  18 + uploadTab: 'Oplaai',
  19 + urlMissing: 'Die URL na die afbeelding ontbreek.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'af', {
  6 + alt: 'Alternatiewe teks',
  7 + btnUpload: 'Stuur na bediener',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Afbeelding informasie',
  11 + lockRatio: 'Vaste proporsie',
  12 + menu: 'Afbeelding eienskappe',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Herstel grootte',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Afbeelding eienskappe',
  18 + uploadTab: 'Oplaai',
  19 + urlMissing: 'Die URL na die afbeelding ontbreek.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ar', {
  6 + alt: 'عنوان الصورة',
  7 + btnUpload: 'أرسلها للخادم',
  8 + captioned: 'صورة ذات اسم',
  9 + captionPlaceholder: 'تسمية',
  10 + infoTab: 'معلومات الصورة',
  11 + lockRatio: 'تناسق الحجم',
  12 + menu: 'خصائص الصورة',
  13 + pathName: 'صورة',
  14 + pathNameCaption: 'تسمية',
  15 + resetSize: 'إستعادة الحجم الأصلي',
  16 + resizer: 'انقر ثم اسحب للتحجيم',
  17 + title: 'خصائص الصورة',
  18 + uploadTab: 'رفع',
  19 + urlMissing: 'عنوان مصدر الصورة مفقود',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ar', {
  6 + alt: 'عنوان الصورة',
  7 + btnUpload: 'أرسلها للخادم',
  8 + captioned: 'صورة ذات اسم',
  9 + captionPlaceholder: 'تسمية',
  10 + infoTab: 'معلومات الصورة',
  11 + lockRatio: 'تناسق الحجم',
  12 + menu: 'خصائص الصورة',
  13 + pathName: 'صورة',
  14 + pathNameCaption: 'تسمية',
  15 + resetSize: 'إستعادة الحجم الأصلي',
  16 + resizer: 'انقر ثم اسحب للتحجيم',
  17 + title: 'خصائص الصورة',
  18 + uploadTab: 'رفع',
  19 + urlMissing: 'عنوان مصدر الصورة مفقود',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'az', {
  6 + alt: 'Alternativ mətn',
  7 + btnUpload: 'Serverə göndər',
  8 + captioned: 'Altyazı olan şəkil',
  9 + captionPlaceholder: 'Altyazı',
  10 + infoTab: 'Şəkil haqqında məlumat',
  11 + lockRatio: 'Ölçülərin nisbəti saxla',
  12 + menu: 'Şəklin seçimləri',
  13 + pathName: 'Şəkil',
  14 + pathNameCaption: 'Altyazı',
  15 + resetSize: 'Ölçüləri qaytar',
  16 + resizer: 'Ölçülər dəyişmək üçün tıklayın və aparın',
  17 + title: 'Şəklin seçimləri',
  18 + uploadTab: 'Serverə yüklə',
  19 + urlMissing: 'Şəklin ünvanı yanlışdır.',
  20 + altMissing: 'Alternativ mətn tapılmayıb'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'az', {
  6 + alt: 'Alternativ mətn',
  7 + btnUpload: 'Serverə göndər',
  8 + captioned: 'Altyazı olan şəkil',
  9 + captionPlaceholder: 'Altyazı',
  10 + infoTab: 'Şəkil haqqında məlumat',
  11 + lockRatio: 'Ölçülərin nisbəti saxla',
  12 + menu: 'Şəklin seçimləri',
  13 + pathName: 'Şəkil',
  14 + pathNameCaption: 'Altyazı',
  15 + resetSize: 'Ölçüləri qaytar',
  16 + resizer: 'Ölçülər dəyişmək üçün tıklayın və aparın',
  17 + title: 'Şəklin seçimləri',
  18 + uploadTab: 'Serverə yüklə',
  19 + urlMissing: 'Şəklin ünvanı yanlışdır.',
  20 + altMissing: 'Alternativ mətn tapılmayıb'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'bg', {
  6 + alt: 'Алтернативен текст',
  7 + btnUpload: 'Изпрати я на сървъра',
  8 + captioned: 'Надписано изображение',
  9 + captionPlaceholder: 'Надпис',
  10 + infoTab: 'Детайли за изображението',
  11 + lockRatio: 'Заключване на съотношението',
  12 + menu: 'Настройки на изображението',
  13 + pathName: 'изображение',
  14 + pathNameCaption: 'надпис',
  15 + resetSize: 'Нулиране на размер',
  16 + resizer: 'Кликни и влачи, за да преоразмериш',
  17 + title: 'Настройки на изображението',
  18 + uploadTab: 'Качване',
  19 + urlMissing: 'URL адреса на изображението липсва.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'bg', {
  6 + alt: 'Алтернативен текст',
  7 + btnUpload: 'Изпрати я на сървъра',
  8 + captioned: 'Надписано изображение',
  9 + captionPlaceholder: 'Надпис',
  10 + infoTab: 'Детайли за изображението',
  11 + lockRatio: 'Заключване на съотношението',
  12 + menu: 'Настройки на изображението',
  13 + pathName: 'изображение',
  14 + pathNameCaption: 'надпис',
  15 + resetSize: 'Нулиране на размер',
  16 + resizer: 'Кликни и влачи, за да преоразмериш',
  17 + title: 'Настройки на изображението',
  18 + uploadTab: 'Качване',
  19 + urlMissing: 'URL адреса на изображението липсва.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'bn', {
  6 + alt: 'বিকল্প টেক্সট',
  7 + btnUpload: 'ইহাকে সার্ভারে প্রেরন কর',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'ছবির তথ্য',
  11 + lockRatio: 'অনুপাত লক কর',
  12 + menu: 'ছবির প্রোপার্টি',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'সাইজ পূর্বাবস্থায় ফিরিয়ে দাও',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'ছবির প্রোপার্টি',
  18 + uploadTab: 'আপলোড',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'bn', {
  6 + alt: 'বিকল্প টেক্সট',
  7 + btnUpload: 'ইহাকে সার্ভারে প্রেরন কর',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'ছবির তথ্য',
  11 + lockRatio: 'অনুপাত লক কর',
  12 + menu: 'ছবির প্রোপার্টি',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'সাইজ পূর্বাবস্থায় ফিরিয়ে দাও',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'ছবির প্রোপার্টি',
  18 + uploadTab: 'আপলোড',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'bs', {
  6 + alt: 'Tekst na slici',
  7 + btnUpload: 'Šalji na server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Info slike',
  11 + lockRatio: 'Zakljuèaj odnos',
  12 + menu: 'Svojstva slike',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Resetuj dimenzije',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Svojstva slike',
  18 + uploadTab: 'Šalji',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'bs', {
  6 + alt: 'Tekst na slici',
  7 + btnUpload: 'Šalji na server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Info slike',
  11 + lockRatio: 'Zakljuèaj odnos',
  12 + menu: 'Svojstva slike',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Resetuj dimenzije',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Svojstva slike',
  18 + uploadTab: 'Šalji',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ca', {
  6 + alt: 'Text alternatiu',
  7 + btnUpload: 'Envia-la al servidor',
  8 + captioned: 'Imatge amb subtítol',
  9 + captionPlaceholder: 'Títol',
  10 + infoTab: 'Informació de la imatge',
  11 + lockRatio: 'Bloqueja les proporcions',
  12 + menu: 'Propietats de la imatge',
  13 + pathName: 'imatge',
  14 + pathNameCaption: 'subtítol',
  15 + resetSize: 'Restaura la mida',
  16 + resizer: 'Clicar i arrossegar per redimensionar',
  17 + title: 'Propietats de la imatge',
  18 + uploadTab: 'Puja',
  19 + urlMissing: 'Falta la URL de la imatge.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ca', {
  6 + alt: 'Text alternatiu',
  7 + btnUpload: 'Envia-la al servidor',
  8 + captioned: 'Imatge amb subtítol',
  9 + captionPlaceholder: 'Títol',
  10 + infoTab: 'Informació de la imatge',
  11 + lockRatio: 'Bloqueja les proporcions',
  12 + menu: 'Propietats de la imatge',
  13 + pathName: 'imatge',
  14 + pathNameCaption: 'subtítol',
  15 + resetSize: 'Restaura la mida',
  16 + resizer: 'Clicar i arrossegar per redimensionar',
  17 + title: 'Propietats de la imatge',
  18 + uploadTab: 'Puja',
  19 + urlMissing: 'Falta la URL de la imatge.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'cs', {
  6 + alt: 'Alternativní text',
  7 + btnUpload: 'Odeslat na server',
  8 + captioned: 'Obrázek s popisem',
  9 + captionPlaceholder: 'Popis',
  10 + infoTab: 'Informace o obrázku',
  11 + lockRatio: 'Zámek',
  12 + menu: 'Vlastnosti obrázku',
  13 + pathName: 'Obrázek',
  14 + pathNameCaption: 'Popis',
  15 + resetSize: 'Původní velikost',
  16 + resizer: 'Klepněte a táhněte pro změnu velikosti',
  17 + title: 'Vlastnosti obrázku',
  18 + uploadTab: 'Odeslat',
  19 + urlMissing: 'Zadané URL zdroje obrázku nebylo nalezeno.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'cs', {
  6 + alt: 'Alternativní text',
  7 + btnUpload: 'Odeslat na server',
  8 + captioned: 'Obrázek s popisem',
  9 + captionPlaceholder: 'Popis',
  10 + infoTab: 'Informace o obrázku',
  11 + lockRatio: 'Zámek',
  12 + menu: 'Vlastnosti obrázku',
  13 + pathName: 'Obrázek',
  14 + pathNameCaption: 'Popis',
  15 + resetSize: 'Původní velikost',
  16 + resizer: 'Klepněte a táhněte pro změnu velikosti',
  17 + title: 'Vlastnosti obrázku',
  18 + uploadTab: 'Odeslat',
  19 + urlMissing: 'Zadané URL zdroje obrázku nebylo nalezeno.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'cy', {
  6 + alt: 'Testun Amgen',
  7 + btnUpload: 'Anfon i\'r Gweinydd',
  8 + captioned: 'Delwedd â phennawd',
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Gwyb Delwedd',
  11 + lockRatio: 'Cloi Cymhareb',
  12 + menu: 'Priodweddau Delwedd',
  13 + pathName: 'delwedd',
  14 + pathNameCaption: 'pennawd',
  15 + resetSize: 'Ailosod Maint',
  16 + resizer: 'Clicio a llusgo i ail-meintio',
  17 + title: 'Priodweddau Delwedd',
  18 + uploadTab: 'Lanlwytho',
  19 + urlMissing: 'URL gwreiddiol y ddelwedd ar goll.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'cy', {
  6 + alt: 'Testun Amgen',
  7 + btnUpload: 'Anfon i\'r Gweinydd',
  8 + captioned: 'Delwedd â phennawd',
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Gwyb Delwedd',
  11 + lockRatio: 'Cloi Cymhareb',
  12 + menu: 'Priodweddau Delwedd',
  13 + pathName: 'delwedd',
  14 + pathNameCaption: 'pennawd',
  15 + resetSize: 'Ailosod Maint',
  16 + resizer: 'Clicio a llusgo i ail-meintio',
  17 + title: 'Priodweddau Delwedd',
  18 + uploadTab: 'Lanlwytho',
  19 + urlMissing: 'URL gwreiddiol y ddelwedd ar goll.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'da', {
  6 + alt: 'Alternativ tekst',
  7 + btnUpload: 'Upload fil til serveren',
  8 + captioned: 'Tekstet billede',
  9 + captionPlaceholder: 'Tekst',
  10 + infoTab: 'Generelt',
  11 + lockRatio: 'Lås størrelsesforhold',
  12 + menu: 'Egenskaber for billede',
  13 + pathName: 'billede',
  14 + pathNameCaption: 'tekst',
  15 + resetSize: 'Nulstil størrelse',
  16 + resizer: 'Klik og træk for at ændre størrelsen',
  17 + title: 'Egenskaber for billede',
  18 + uploadTab: 'Upload',
  19 + urlMissing: 'Kilde på billed-URL mangler',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'da', {
  6 + alt: 'Alternativ tekst',
  7 + btnUpload: 'Upload fil til serveren',
  8 + captioned: 'Tekstet billede',
  9 + captionPlaceholder: 'Tekst',
  10 + infoTab: 'Generelt',
  11 + lockRatio: 'Lås størrelsesforhold',
  12 + menu: 'Egenskaber for billede',
  13 + pathName: 'billede',
  14 + pathNameCaption: 'tekst',
  15 + resetSize: 'Nulstil størrelse',
  16 + resizer: 'Klik og træk for at ændre størrelsen',
  17 + title: 'Egenskaber for billede',
  18 + uploadTab: 'Upload',
  19 + urlMissing: 'Kilde på billed-URL mangler',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'de-ch', {
  6 + alt: 'Alternativer Text',
  7 + btnUpload: 'Zum Server senden',
  8 + captioned: 'Bild mit Überschrift',
  9 + captionPlaceholder: 'Überschrift',
  10 + infoTab: 'Bildinfo',
  11 + lockRatio: 'Größenverhältnis beibehalten',
  12 + menu: 'Bildeigenschaften',
  13 + pathName: 'Bild',
  14 + pathNameCaption: 'Überschrift',
  15 + resetSize: 'Grösse zurücksetzen',
  16 + resizer: 'Zum Vergrössern auswählen und ziehen',
  17 + title: 'Bild-Eigenschaften',
  18 + uploadTab: 'Hochladen',
  19 + urlMissing: 'Bildquellen-URL fehlt.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'de-ch', {
  6 + alt: 'Alternativer Text',
  7 + btnUpload: 'Zum Server senden',
  8 + captioned: 'Bild mit Überschrift',
  9 + captionPlaceholder: 'Überschrift',
  10 + infoTab: 'Bildinfo',
  11 + lockRatio: 'Größenverhältnis beibehalten',
  12 + menu: 'Bildeigenschaften',
  13 + pathName: 'Bild',
  14 + pathNameCaption: 'Überschrift',
  15 + resetSize: 'Grösse zurücksetzen',
  16 + resizer: 'Zum Vergrössern auswählen und ziehen',
  17 + title: 'Bild-Eigenschaften',
  18 + uploadTab: 'Hochladen',
  19 + urlMissing: 'Bildquellen-URL fehlt.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'de', {
  6 + alt: 'Alternativer Text',
  7 + btnUpload: 'Zum Server senden',
  8 + captioned: 'Bild mit Überschrift',
  9 + captionPlaceholder: 'Überschrift',
  10 + infoTab: 'Bildinfo',
  11 + lockRatio: 'Größenverhältnis beibehalten',
  12 + menu: 'Bildeigenschaften',
  13 + pathName: 'Bild',
  14 + pathNameCaption: 'Überschrift',
  15 + resetSize: 'Größe zurücksetzen',
  16 + resizer: 'Zum Vergrößern auswählen und ziehen',
  17 + title: 'Bild-Eigenschaften',
  18 + uploadTab: 'Hochladen',
  19 + urlMissing: 'Bildquellen-URL fehlt.',
  20 + altMissing: 'Alternativer Text fehlt.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'de', {
  6 + alt: 'Alternativer Text',
  7 + btnUpload: 'Zum Server senden',
  8 + captioned: 'Bild mit Überschrift',
  9 + captionPlaceholder: 'Überschrift',
  10 + infoTab: 'Bildinfo',
  11 + lockRatio: 'Größenverhältnis beibehalten',
  12 + menu: 'Bildeigenschaften',
  13 + pathName: 'Bild',
  14 + pathNameCaption: 'Überschrift',
  15 + resetSize: 'Größe zurücksetzen',
  16 + resizer: 'Zum Vergrößern auswählen und ziehen',
  17 + title: 'Bild-Eigenschaften',
  18 + uploadTab: 'Hochladen',
  19 + urlMissing: 'Bildquellen-URL fehlt.',
  20 + altMissing: 'Alternativer Text fehlt.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'el', {
  6 + alt: 'Εναλλακτικό Κείμενο',
  7 + btnUpload: 'Αποστολή στον Διακομιστή',
  8 + captioned: 'Εικόνα με λεζάντα',
  9 + captionPlaceholder: 'Λεζάντα',
  10 + infoTab: 'Πληροφορίες Εικόνας',
  11 + lockRatio: 'Κλείδωμα Αναλογίας',
  12 + menu: 'Ιδιότητες Εικόνας',
  13 + pathName: 'εικόνα',
  14 + pathNameCaption: 'λεζάντα',
  15 + resetSize: 'Επαναφορά Αρχικού Μεγέθους',
  16 + resizer: 'Κάνετε κλικ και σύρετε το ποντίκι για να αλλάξετε το μέγεθος',
  17 + title: 'Ιδιότητες Εικόνας',
  18 + uploadTab: 'Αποστολή',
  19 + urlMissing: 'Λείπει το πηγαίο URL της εικόνας.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'el', {
  6 + alt: 'Εναλλακτικό Κείμενο',
  7 + btnUpload: 'Αποστολή στον Διακομιστή',
  8 + captioned: 'Εικόνα με λεζάντα',
  9 + captionPlaceholder: 'Λεζάντα',
  10 + infoTab: 'Πληροφορίες Εικόνας',
  11 + lockRatio: 'Κλείδωμα Αναλογίας',
  12 + menu: 'Ιδιότητες Εικόνας',
  13 + pathName: 'εικόνα',
  14 + pathNameCaption: 'λεζάντα',
  15 + resetSize: 'Επαναφορά Αρχικού Μεγέθους',
  16 + resizer: 'Κάνετε κλικ και σύρετε το ποντίκι για να αλλάξετε το μέγεθος',
  17 + title: 'Ιδιότητες Εικόνας',
  18 + uploadTab: 'Αποστολή',
  19 + urlMissing: 'Λείπει το πηγαίο URL της εικόνας.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'en-au', {
  6 + alt: 'Alternative Text',
  7 + btnUpload: 'Send it to the Server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Image Info',
  11 + lockRatio: 'Lock Ratio',
  12 + menu: 'Image Properties',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Reset Size',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Image Properties',
  18 + uploadTab: 'Upload',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'en-au', {
  6 + alt: 'Alternative Text',
  7 + btnUpload: 'Send it to the Server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Image Info',
  11 + lockRatio: 'Lock Ratio',
  12 + menu: 'Image Properties',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Reset Size',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Image Properties',
  18 + uploadTab: 'Upload',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'en-ca', {
  6 + alt: 'Alternative Text',
  7 + btnUpload: 'Send it to the Server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Image Info',
  11 + lockRatio: 'Lock Ratio',
  12 + menu: 'Image Properties',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Reset Size',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Image Properties',
  18 + uploadTab: 'Upload',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'en-ca', {
  6 + alt: 'Alternative Text',
  7 + btnUpload: 'Send it to the Server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Image Info',
  11 + lockRatio: 'Lock Ratio',
  12 + menu: 'Image Properties',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Reset Size',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Image Properties',
  18 + uploadTab: 'Upload',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'en-gb', {
  6 + alt: 'Alternative Text',
  7 + btnUpload: 'Send it to the Server',
  8 + captioned: 'Captioned image',
  9 + captionPlaceholder: 'Caption',
  10 + infoTab: 'Image Info',
  11 + lockRatio: 'Lock Ratio',
  12 + menu: 'Image Properties',
  13 + pathName: 'image',
  14 + pathNameCaption: 'caption',
  15 + resetSize: 'Reset Size',
  16 + resizer: 'Click and drag to resize',
  17 + title: 'Image Properties',
  18 + uploadTab: 'Upload',
  19 + urlMissing: 'Image source URL is missing.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'en-gb', {
  6 + alt: 'Alternative Text',
  7 + btnUpload: 'Send it to the Server',
  8 + captioned: 'Captioned image',
  9 + captionPlaceholder: 'Caption',
  10 + infoTab: 'Image Info',
  11 + lockRatio: 'Lock Ratio',
  12 + menu: 'Image Properties',
  13 + pathName: 'image',
  14 + pathNameCaption: 'caption',
  15 + resetSize: 'Reset Size',
  16 + resizer: 'Click and drag to resize',
  17 + title: 'Image Properties',
  18 + uploadTab: 'Upload',
  19 + urlMissing: 'Image source URL is missing.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'en', {
  6 + alt: 'Alternative Text',
  7 + btnUpload: 'Send it to the Server',
  8 + captioned: 'Captioned image',
  9 + captionPlaceholder: 'Caption',
  10 + infoTab: 'Image Info',
  11 + lockRatio: 'Lock Ratio',
  12 + menu: 'Image Properties',
  13 + pathName: 'image',
  14 + pathNameCaption: 'caption',
  15 + resetSize: 'Reset Size',
  16 + resizer: 'Click and drag to resize',
  17 + title: 'Image Properties',
  18 + uploadTab: 'Upload',
  19 + urlMissing: 'Image source URL is missing.',
  20 + altMissing: 'Alternative text is missing.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'en', {
  6 + alt: 'Alternative Text',
  7 + btnUpload: 'Send it to the Server',
  8 + captioned: 'Captioned image',
  9 + captionPlaceholder: 'Caption',
  10 + infoTab: 'Image Info',
  11 + lockRatio: 'Lock Ratio',
  12 + menu: 'Image Properties',
  13 + pathName: 'image',
  14 + pathNameCaption: 'caption',
  15 + resetSize: 'Reset Size',
  16 + resizer: 'Click and drag to resize',
  17 + title: 'Image Properties',
  18 + uploadTab: 'Upload',
  19 + urlMissing: 'Image source URL is missing.',
  20 + altMissing: 'Alternative text is missing.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'eo', {
  6 + alt: 'Anstataŭiga Teksto',
  7 + btnUpload: 'Sendu al Servilo',
  8 + captioned: 'Bildo kun apudskribo',
  9 + captionPlaceholder: 'Apudskribo',
  10 + infoTab: 'Informoj pri Bildo',
  11 + lockRatio: 'Konservi Proporcion',
  12 + menu: 'Atributoj de Bildo',
  13 + pathName: 'bildo',
  14 + pathNameCaption: 'apudskribo',
  15 + resetSize: 'Origina Grando',
  16 + resizer: 'Kliki kaj treni por ŝanĝi la grandon',
  17 + title: 'Atributoj de Bildo',
  18 + uploadTab: 'Alŝuti',
  19 + urlMissing: 'La fontretadreso de la bildo mankas.',
  20 + altMissing: 'Alternativa teksto mankas.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'eo', {
  6 + alt: 'Anstataŭiga Teksto',
  7 + btnUpload: 'Sendu al Servilo',
  8 + captioned: 'Bildo kun apudskribo',
  9 + captionPlaceholder: 'Apudskribo',
  10 + infoTab: 'Informoj pri Bildo',
  11 + lockRatio: 'Konservi Proporcion',
  12 + menu: 'Atributoj de Bildo',
  13 + pathName: 'bildo',
  14 + pathNameCaption: 'apudskribo',
  15 + resetSize: 'Origina Grando',
  16 + resizer: 'Kliki kaj treni por ŝanĝi la grandon',
  17 + title: 'Atributoj de Bildo',
  18 + uploadTab: 'Alŝuti',
  19 + urlMissing: 'La fontretadreso de la bildo mankas.',
  20 + altMissing: 'Alternativa teksto mankas.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'es-mx', {
  6 + alt: 'Texto alternativo',
  7 + btnUpload: 'Enviar al servidor',
  8 + captioned: 'Imagen subtitulada',
  9 + captionPlaceholder: 'Subtítulo',
  10 + infoTab: 'Información de la imagen',
  11 + lockRatio: 'Bloquear aspecto',
  12 + menu: 'Propiedades de la imagen',
  13 + pathName: 'imagen',
  14 + pathNameCaption: 'subtítulo',
  15 + resetSize: 'Reiniciar tamaño',
  16 + resizer: 'Presiona y arrastra para redimensionar',
  17 + title: 'Propiedades de imagen',
  18 + uploadTab: 'Cargar',
  19 + urlMissing: 'Falta la URL de origen de la imagen.',
  20 + altMissing: 'Falta texto alternativo.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'es-mx', {
  6 + alt: 'Texto alternativo',
  7 + btnUpload: 'Enviar al servidor',
  8 + captioned: 'Imagen subtitulada',
  9 + captionPlaceholder: 'Subtítulo',
  10 + infoTab: 'Información de la imagen',
  11 + lockRatio: 'Bloquear aspecto',
  12 + menu: 'Propiedades de la imagen',
  13 + pathName: 'imagen',
  14 + pathNameCaption: 'subtítulo',
  15 + resetSize: 'Reiniciar tamaño',
  16 + resizer: 'Presiona y arrastra para redimensionar',
  17 + title: 'Propiedades de imagen',
  18 + uploadTab: 'Cargar',
  19 + urlMissing: 'Falta la URL de origen de la imagen.',
  20 + altMissing: 'Falta texto alternativo.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'es', {
  6 + alt: 'Texto Alternativo',
  7 + btnUpload: 'Enviar al Servidor',
  8 + captioned: 'Imagen subtitulada',
  9 + captionPlaceholder: 'Leyenda',
  10 + infoTab: 'Información de Imagen',
  11 + lockRatio: 'Proporcional',
  12 + menu: 'Propiedades de Imagen',
  13 + pathName: 'image',
  14 + pathNameCaption: 'subtítulo',
  15 + resetSize: 'Tamaño Original',
  16 + resizer: 'Dar clic y arrastrar para cambiar tamaño',
  17 + title: 'Propiedades de Imagen',
  18 + uploadTab: 'Cargar',
  19 + urlMissing: 'Debe indicar la URL de la imagen.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'es', {
  6 + alt: 'Texto Alternativo',
  7 + btnUpload: 'Enviar al Servidor',
  8 + captioned: 'Imagen subtitulada',
  9 + captionPlaceholder: 'Leyenda',
  10 + infoTab: 'Información de Imagen',
  11 + lockRatio: 'Proporcional',
  12 + menu: 'Propiedades de Imagen',
  13 + pathName: 'image',
  14 + pathNameCaption: 'subtítulo',
  15 + resetSize: 'Tamaño Original',
  16 + resizer: 'Dar clic y arrastrar para cambiar tamaño',
  17 + title: 'Propiedades de Imagen',
  18 + uploadTab: 'Cargar',
  19 + urlMissing: 'Debe indicar la URL de la imagen.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'et', {
  6 + alt: 'Alternatiivne tekst',
  7 + btnUpload: 'Saada serverisse',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Pildi info',
  11 + lockRatio: 'Lukusta kuvasuhe',
  12 + menu: 'Pildi omadused',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Lähtesta suurus',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Pildi omadused',
  18 + uploadTab: 'Lae üles',
  19 + urlMissing: 'Pildi lähte-URL on puudu.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'et', {
  6 + alt: 'Alternatiivne tekst',
  7 + btnUpload: 'Saada serverisse',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Pildi info',
  11 + lockRatio: 'Lukusta kuvasuhe',
  12 + menu: 'Pildi omadused',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Lähtesta suurus',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Pildi omadused',
  18 + uploadTab: 'Lae üles',
  19 + urlMissing: 'Pildi lähte-URL on puudu.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'eu', {
  6 + alt: 'Ordezko testua',
  7 + btnUpload: 'Bidali zerbitzarira',
  8 + captioned: 'Argazki oina',
  9 + captionPlaceholder: 'Argazki oina',
  10 + infoTab: 'Irudiaren informazioa',
  11 + lockRatio: 'Blokeatu erlazioa',
  12 + menu: 'Irudiaren propietateak',
  13 + pathName: 'Irudia',
  14 + pathNameCaption: 'Argazki oina',
  15 + resetSize: 'Berrezarri tamaina',
  16 + resizer: 'Klikatu eta arrastatu tamainaz aldatzeko',
  17 + title: 'Irudiaren propietateak',
  18 + uploadTab: 'Kargatu',
  19 + urlMissing: 'Irudiaren iturburuaren URLa falta da.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'eu', {
  6 + alt: 'Ordezko testua',
  7 + btnUpload: 'Bidali zerbitzarira',
  8 + captioned: 'Argazki oina',
  9 + captionPlaceholder: 'Argazki oina',
  10 + infoTab: 'Irudiaren informazioa',
  11 + lockRatio: 'Blokeatu erlazioa',
  12 + menu: 'Irudiaren propietateak',
  13 + pathName: 'Irudia',
  14 + pathNameCaption: 'Argazki oina',
  15 + resetSize: 'Berrezarri tamaina',
  16 + resizer: 'Klikatu eta arrastatu tamainaz aldatzeko',
  17 + title: 'Irudiaren propietateak',
  18 + uploadTab: 'Kargatu',
  19 + urlMissing: 'Irudiaren iturburuaren URLa falta da.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'fa', {
  6 + alt: 'متن جایگزین',
  7 + btnUpload: 'به سرور بفرست',
  8 + captioned: 'تصویر زیرنویس شده',
  9 + captionPlaceholder: 'عنوان',
  10 + infoTab: 'اطلاعات تصویر',
  11 + lockRatio: 'قفل کردن نسبت',
  12 + menu: 'ویژگی​های تصویر',
  13 + pathName: 'تصویر',
  14 + pathNameCaption: 'عنوان',
  15 + resetSize: 'بازنشانی اندازه',
  16 + resizer: 'کلیک و کشیدن برای تغییر اندازه',
  17 + title: 'ویژگی​های تصویر',
  18 + uploadTab: 'بالاگذاری',
  19 + urlMissing: 'آدرس URL اصلی تصویر یافت نشد.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'fa', {
  6 + alt: 'متن جایگزین',
  7 + btnUpload: 'به سرور بفرست',
  8 + captioned: 'تصویر زیرنویس شده',
  9 + captionPlaceholder: 'عنوان',
  10 + infoTab: 'اطلاعات تصویر',
  11 + lockRatio: 'قفل کردن نسبت',
  12 + menu: 'ویژگی​های تصویر',
  13 + pathName: 'تصویر',
  14 + pathNameCaption: 'عنوان',
  15 + resetSize: 'بازنشانی اندازه',
  16 + resizer: 'کلیک و کشیدن برای تغییر اندازه',
  17 + title: 'ویژگی​های تصویر',
  18 + uploadTab: 'بالاگذاری',
  19 + urlMissing: 'آدرس URL اصلی تصویر یافت نشد.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'fi', {
  6 + alt: 'Vaihtoehtoinen teksti',
  7 + btnUpload: 'Lähetä palvelimelle',
  8 + captioned: 'Kuva kuvatekstillä',
  9 + captionPlaceholder: 'Kuvateksti',
  10 + infoTab: 'Kuvan tiedot',
  11 + lockRatio: 'Lukitse suhteet',
  12 + menu: 'Kuvan ominaisuudet',
  13 + pathName: 'kuva',
  14 + pathNameCaption: 'kuvateksti',
  15 + resetSize: 'Alkuperäinen koko',
  16 + resizer: 'Klikkaa ja raahaa muuttaaksesi kokoa',
  17 + title: 'Kuvan ominaisuudet',
  18 + uploadTab: 'Lisää tiedosto',
  19 + urlMissing: 'Kuvan lähdeosoite puuttuu.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'fi', {
  6 + alt: 'Vaihtoehtoinen teksti',
  7 + btnUpload: 'Lähetä palvelimelle',
  8 + captioned: 'Kuva kuvatekstillä',
  9 + captionPlaceholder: 'Kuvateksti',
  10 + infoTab: 'Kuvan tiedot',
  11 + lockRatio: 'Lukitse suhteet',
  12 + menu: 'Kuvan ominaisuudet',
  13 + pathName: 'kuva',
  14 + pathNameCaption: 'kuvateksti',
  15 + resetSize: 'Alkuperäinen koko',
  16 + resizer: 'Klikkaa ja raahaa muuttaaksesi kokoa',
  17 + title: 'Kuvan ominaisuudet',
  18 + uploadTab: 'Lisää tiedosto',
  19 + urlMissing: 'Kuvan lähdeosoite puuttuu.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'fo', {
  6 + alt: 'Alternativur tekstur',
  7 + btnUpload: 'Send til ambætaran',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Myndaupplýsingar',
  11 + lockRatio: 'Læs lutfallið',
  12 + menu: 'Myndaeginleikar',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Upprunastødd',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Myndaeginleikar',
  18 + uploadTab: 'Send til ambætaran',
  19 + urlMissing: 'URL til mynd manglar.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'fo', {
  6 + alt: 'Alternativur tekstur',
  7 + btnUpload: 'Send til ambætaran',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Myndaupplýsingar',
  11 + lockRatio: 'Læs lutfallið',
  12 + menu: 'Myndaeginleikar',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Upprunastødd',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Myndaeginleikar',
  18 + uploadTab: 'Send til ambætaran',
  19 + urlMissing: 'URL til mynd manglar.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'fr-ca', {
  6 + alt: 'Texte alternatif',
  7 + btnUpload: 'Envoyer sur le serveur',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Informations sur l\'image2',
  11 + lockRatio: 'Verrouiller les proportions',
  12 + menu: 'Propriétés de l\'image2',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Taille originale',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Propriétés de l\'image2',
  18 + uploadTab: 'Téléverser',
  19 + urlMissing: 'L\'URL de la source de l\'image est manquant.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'fr-ca', {
  6 + alt: 'Texte alternatif',
  7 + btnUpload: 'Envoyer sur le serveur',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Informations sur l\'image2',
  11 + lockRatio: 'Verrouiller les proportions',
  12 + menu: 'Propriétés de l\'image2',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Taille originale',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Propriétés de l\'image2',
  18 + uploadTab: 'Téléverser',
  19 + urlMissing: 'L\'URL de la source de l\'image est manquant.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'fr', {
  6 + alt: 'Texte alternatif',
  7 + btnUpload: 'Envoyer sur le serveur',
  8 + captioned: 'Image légendée',
  9 + captionPlaceholder: 'Légende',
  10 + infoTab: 'Informations sur l\'image',
  11 + lockRatio: 'Conserver les proportions',
  12 + menu: 'Propriétés de l\'image',
  13 + pathName: 'image',
  14 + pathNameCaption: 'légende',
  15 + resetSize: 'Réinitialiser la taille',
  16 + resizer: 'Cliquer et glisser pour redimensionner',
  17 + title: 'Propriétés de l\'image',
  18 + uploadTab: 'Téléverser',
  19 + urlMissing: 'L\'URL source de l\'image est manquante.',
  20 + altMissing: 'Vous n\'avez pas indiqué de texte de remplacement.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'fr', {
  6 + alt: 'Texte alternatif',
  7 + btnUpload: 'Envoyer sur le serveur',
  8 + captioned: 'Image légendée',
  9 + captionPlaceholder: 'Légende',
  10 + infoTab: 'Informations sur l\'image',
  11 + lockRatio: 'Conserver les proportions',
  12 + menu: 'Propriétés de l\'image',
  13 + pathName: 'image',
  14 + pathNameCaption: 'légende',
  15 + resetSize: 'Réinitialiser la taille',
  16 + resizer: 'Cliquer et glisser pour redimensionner',
  17 + title: 'Propriétés de l\'image',
  18 + uploadTab: 'Téléverser',
  19 + urlMissing: 'L\'URL source de l\'image est manquante.',
  20 + altMissing: 'Vous n\'avez pas indiqué de texte de remplacement.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'gl', {
  6 + alt: 'Texto alternativo',
  7 + btnUpload: 'Enviar ao servidor',
  8 + captioned: 'Imaxe con lenda',
  9 + captionPlaceholder: 'Lenda',
  10 + infoTab: 'Información da imaxe',
  11 + lockRatio: 'Proporcional',
  12 + menu: 'Propiedades da imaxe',
  13 + pathName: 'Imaxe',
  14 + pathNameCaption: 'lenda',
  15 + resetSize: 'Tamaño orixinal',
  16 + resizer: 'Prema e arrastre para axustar o tamaño',
  17 + title: 'Propiedades da imaxe',
  18 + uploadTab: 'Cargar',
  19 + urlMissing: 'Non se atopa o URL da imaxe.',
  20 + altMissing: 'Non foi posíbel atopar o texto alternativo.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'gl', {
  6 + alt: 'Texto alternativo',
  7 + btnUpload: 'Enviar ao servidor',
  8 + captioned: 'Imaxe con lenda',
  9 + captionPlaceholder: 'Lenda',
  10 + infoTab: 'Información da imaxe',
  11 + lockRatio: 'Proporcional',
  12 + menu: 'Propiedades da imaxe',
  13 + pathName: 'Imaxe',
  14 + pathNameCaption: 'lenda',
  15 + resetSize: 'Tamaño orixinal',
  16 + resizer: 'Prema e arrastre para axustar o tamaño',
  17 + title: 'Propiedades da imaxe',
  18 + uploadTab: 'Cargar',
  19 + urlMissing: 'Non se atopa o URL da imaxe.',
  20 + altMissing: 'Non foi posíbel atopar o texto alternativo.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'gu', {
  6 + alt: 'ઑલ્ટર્નટ ટેક્સ્ટ',
  7 + btnUpload: 'આ સર્વરને મોકલવું',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'ચિત્ર ની જાણકારી',
  11 + lockRatio: 'લૉક ગુણોત્તર',
  12 + menu: 'ચિત્રના ગુણ',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'રીસેટ સાઇઝ',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'ચિત્રના ગુણ',
  18 + uploadTab: 'અપલોડ',
  19 + urlMissing: 'ઈમેજની મૂળ URL છે નહી.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'gu', {
  6 + alt: 'ઑલ્ટર્નટ ટેક્સ્ટ',
  7 + btnUpload: 'આ સર્વરને મોકલવું',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'ચિત્ર ની જાણકારી',
  11 + lockRatio: 'લૉક ગુણોત્તર',
  12 + menu: 'ચિત્રના ગુણ',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'રીસેટ સાઇઝ',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'ચિત્રના ગુણ',
  18 + uploadTab: 'અપલોડ',
  19 + urlMissing: 'ઈમેજની મૂળ URL છે નહી.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'he', {
  6 + alt: 'טקסט חלופי',
  7 + btnUpload: 'שליחה לשרת',
  8 + captioned: 'כותרת תמונה',
  9 + captionPlaceholder: 'כותרת',
  10 + infoTab: 'מידע על התמונה',
  11 + lockRatio: 'נעילת היחס',
  12 + menu: 'תכונות התמונה',
  13 + pathName: 'תמונה',
  14 + pathNameCaption: 'כותרת',
  15 + resetSize: 'איפוס הגודל',
  16 + resizer: 'לחץ וגרור לשינוי הגודל',
  17 + title: 'מאפייני התמונה',
  18 + uploadTab: 'העלאה',
  19 + urlMissing: 'כתובת התמונה חסרה.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'he', {
  6 + alt: 'טקסט חלופי',
  7 + btnUpload: 'שליחה לשרת',
  8 + captioned: 'כותרת תמונה',
  9 + captionPlaceholder: 'כותרת',
  10 + infoTab: 'מידע על התמונה',
  11 + lockRatio: 'נעילת היחס',
  12 + menu: 'תכונות התמונה',
  13 + pathName: 'תמונה',
  14 + pathNameCaption: 'כותרת',
  15 + resetSize: 'איפוס הגודל',
  16 + resizer: 'לחץ וגרור לשינוי הגודל',
  17 + title: 'מאפייני התמונה',
  18 + uploadTab: 'העלאה',
  19 + urlMissing: 'כתובת התמונה חסרה.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'hi', {
  6 + alt: 'वैकल्पिक टेक्स्ट',
  7 + btnUpload: 'इसे सर्वर को भेजें',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'तस्वीर की जानकारी',
  11 + lockRatio: 'लॉक अनुपात',
  12 + menu: 'तस्वीर प्रॉपर्टीज़',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'रीसॅट साइज़',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'तस्वीर प्रॉपर्टीज़',
  18 + uploadTab: 'अपलोड',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'hi', {
  6 + alt: 'वैकल्पिक टेक्स्ट',
  7 + btnUpload: 'इसे सर्वर को भेजें',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'तस्वीर की जानकारी',
  11 + lockRatio: 'लॉक अनुपात',
  12 + menu: 'तस्वीर प्रॉपर्टीज़',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'रीसॅट साइज़',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'तस्वीर प्रॉपर्टीज़',
  18 + uploadTab: 'अपलोड',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'hr', {
  6 + alt: 'Alternativni tekst',
  7 + btnUpload: 'Pošalji na server',
  8 + captioned: 'Titl slike',
  9 + captionPlaceholder: 'Titl',
  10 + infoTab: 'Info slike',
  11 + lockRatio: 'Zaključaj odnos',
  12 + menu: 'Svojstva slika',
  13 + pathName: 'slika',
  14 + pathNameCaption: 'titl',
  15 + resetSize: 'Obriši veličinu',
  16 + resizer: 'Odaberi i povuci za promjenu veličine',
  17 + title: 'Svojstva slika',
  18 + uploadTab: 'Pošalji',
  19 + urlMissing: 'Nedostaje URL slike.',
  20 + altMissing: 'Nedostaje alternativni tekst.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'hr', {
  6 + alt: 'Alternativni tekst',
  7 + btnUpload: 'Pošalji na server',
  8 + captioned: 'Titl slike',
  9 + captionPlaceholder: 'Titl',
  10 + infoTab: 'Info slike',
  11 + lockRatio: 'Zaključaj odnos',
  12 + menu: 'Svojstva slika',
  13 + pathName: 'slika',
  14 + pathNameCaption: 'titl',
  15 + resetSize: 'Obriši veličinu',
  16 + resizer: 'Odaberi i povuci za promjenu veličine',
  17 + title: 'Svojstva slika',
  18 + uploadTab: 'Pošalji',
  19 + urlMissing: 'Nedostaje URL slike.',
  20 + altMissing: 'Nedostaje alternativni tekst.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'hu', {
  6 + alt: 'Buborék szöveg',
  7 + btnUpload: 'Küldés a szerverre',
  8 + captioned: 'Feliratozott kép',
  9 + captionPlaceholder: 'Képfelirat',
  10 + infoTab: 'Alaptulajdonságok',
  11 + lockRatio: 'Arány megtartása',
  12 + menu: 'Kép tulajdonságai',
  13 + pathName: 'kép',
  14 + pathNameCaption: 'felirat',
  15 + resetSize: 'Eredeti méret',
  16 + resizer: 'Kattints és húzz az átméretezéshez',
  17 + title: 'Kép tulajdonságai',
  18 + uploadTab: 'Feltöltés',
  19 + urlMissing: 'Hiányzik a kép URL-je',
  20 + altMissing: 'Az alternatív szöveg hiányzik.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'hu', {
  6 + alt: 'Buborék szöveg',
  7 + btnUpload: 'Küldés a szerverre',
  8 + captioned: 'Feliratozott kép',
  9 + captionPlaceholder: 'Képfelirat',
  10 + infoTab: 'Alaptulajdonságok',
  11 + lockRatio: 'Arány megtartása',
  12 + menu: 'Kép tulajdonságai',
  13 + pathName: 'kép',
  14 + pathNameCaption: 'felirat',
  15 + resetSize: 'Eredeti méret',
  16 + resizer: 'Kattints és húzz az átméretezéshez',
  17 + title: 'Kép tulajdonságai',
  18 + uploadTab: 'Feltöltés',
  19 + urlMissing: 'Hiányzik a kép URL-je',
  20 + altMissing: 'Az alternatív szöveg hiányzik.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'id', {
  6 + alt: 'Teks alternatif',
  7 + btnUpload: 'Kirim ke Server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Info Gambar',
  11 + lockRatio: 'Lock Ratio', // MISSING
  12 + menu: 'Image Properties', // MISSING
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Atur Ulang Ukuran',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Image Properties', // MISSING
  18 + uploadTab: 'Unggah',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'id', {
  6 + alt: 'Teks alternatif',
  7 + btnUpload: 'Kirim ke Server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Info Gambar',
  11 + lockRatio: 'Lock Ratio', // MISSING
  12 + menu: 'Image Properties', // MISSING
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Atur Ulang Ukuran',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Image Properties', // MISSING
  18 + uploadTab: 'Unggah',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'is', {
  6 + alt: 'Baklægur texti',
  7 + btnUpload: 'Hlaða upp',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Almennt',
  11 + lockRatio: 'Festa stærðarhlutfall',
  12 + menu: 'Eigindi myndar',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Reikna stærð',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Eigindi myndar',
  18 + uploadTab: 'Senda upp',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'is', {
  6 + alt: 'Baklægur texti',
  7 + btnUpload: 'Hlaða upp',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Almennt',
  11 + lockRatio: 'Festa stærðarhlutfall',
  12 + menu: 'Eigindi myndar',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Reikna stærð',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Eigindi myndar',
  18 + uploadTab: 'Senda upp',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'it', {
  6 + alt: 'Testo alternativo',
  7 + btnUpload: 'Invia al server',
  8 + captioned: 'Immagine con didascalia',
  9 + captionPlaceholder: 'Didascalia',
  10 + infoTab: 'Informazioni immagine',
  11 + lockRatio: 'Blocca rapporto',
  12 + menu: 'Proprietà immagine',
  13 + pathName: 'immagine',
  14 + pathNameCaption: 'didascalia',
  15 + resetSize: 'Reimposta dimensione',
  16 + resizer: 'Fare clic e trascinare per ridimensionare',
  17 + title: 'Proprietà immagine',
  18 + uploadTab: 'Carica',
  19 + urlMissing: 'Manca l\'URL dell\'immagine.',
  20 + altMissing: 'Testo alternativo mancante.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'it', {
  6 + alt: 'Testo alternativo',
  7 + btnUpload: 'Invia al server',
  8 + captioned: 'Immagine con didascalia',
  9 + captionPlaceholder: 'Didascalia',
  10 + infoTab: 'Informazioni immagine',
  11 + lockRatio: 'Blocca rapporto',
  12 + menu: 'Proprietà immagine',
  13 + pathName: 'immagine',
  14 + pathNameCaption: 'didascalia',
  15 + resetSize: 'Reimposta dimensione',
  16 + resizer: 'Fare clic e trascinare per ridimensionare',
  17 + title: 'Proprietà immagine',
  18 + uploadTab: 'Carica',
  19 + urlMissing: 'Manca l\'URL dell\'immagine.',
  20 + altMissing: 'Testo alternativo mancante.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ja', {
  6 + alt: '代替テキスト',
  7 + btnUpload: 'サーバーに送信',
  8 + captioned: 'キャプションを付ける',
  9 + captionPlaceholder: 'キャプション',
  10 + infoTab: '画像情報',
  11 + lockRatio: '比率を固定',
  12 + menu: '画像のプロパティ',
  13 + pathName: 'image',
  14 + pathNameCaption: 'caption',
  15 + resetSize: 'サイズをリセット',
  16 + resizer: 'ドラッグしてリサイズ',
  17 + title: '画像のプロパティ',
  18 + uploadTab: 'アップロード',
  19 + urlMissing: '画像のURLを入力してください。',
  20 + altMissing: '代替テキストを入力してください。'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ja', {
  6 + alt: '代替テキスト',
  7 + btnUpload: 'サーバーに送信',
  8 + captioned: 'キャプションを付ける',
  9 + captionPlaceholder: 'キャプション',
  10 + infoTab: '画像情報',
  11 + lockRatio: '比率を固定',
  12 + menu: '画像のプロパティ',
  13 + pathName: 'image',
  14 + pathNameCaption: 'caption',
  15 + resetSize: 'サイズをリセット',
  16 + resizer: 'ドラッグしてリサイズ',
  17 + title: '画像のプロパティ',
  18 + uploadTab: 'アップロード',
  19 + urlMissing: '画像のURLを入力してください。',
  20 + altMissing: '代替テキストを入力してください。'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ka', {
  6 + alt: 'სანაცვლო ტექსტი',
  7 + btnUpload: 'სერვერისთვის გაგზავნა',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'სურათის ინფორმცია',
  11 + lockRatio: 'პროპორციის შენარჩუნება',
  12 + menu: 'სურათის პარამეტრები',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'ზომის დაბრუნება',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'სურათის პარამეტრები',
  18 + uploadTab: 'აქაჩვა',
  19 + urlMissing: 'სურათის URL არაა შევსებული.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ka', {
  6 + alt: 'სანაცვლო ტექსტი',
  7 + btnUpload: 'სერვერისთვის გაგზავნა',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'სურათის ინფორმცია',
  11 + lockRatio: 'პროპორციის შენარჩუნება',
  12 + menu: 'სურათის პარამეტრები',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'ზომის დაბრუნება',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'სურათის პარამეტრები',
  18 + uploadTab: 'აქაჩვა',
  19 + urlMissing: 'სურათის URL არაა შევსებული.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'km', {
  6 + alt: 'អត្ថបទជំនួស',
  7 + btnUpload: 'បញ្ជូនទៅកាន់ម៉ាស៊ីនផ្តល់សេវា',
  8 + captioned: 'រូប​ដែល​មាន​ចំណង​ជើង',
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'ពត៌មានអំពីរូបភាព',
  11 + lockRatio: 'ចាក់​សោ​ផល​ធៀប',
  12 + menu: 'លក្ខណៈ​សម្បត្តិ​រូប​ភាព',
  13 + pathName: 'រូបភាព',
  14 + pathNameCaption: 'ចំណងជើង',
  15 + resetSize: 'កំណត់ទំហំឡើងវិញ',
  16 + resizer: 'ចុច​ហើយ​ទាញ​ដើម្បី​ប្ដូរ​ទំហំ',
  17 + title: 'លក្ខណៈ​សម្បត្តិ​រូប​ភាប',
  18 + uploadTab: 'ផ្ទុក​ឡើង',
  19 + urlMissing: 'ខ្វះ URL ប្រភព​រូប​ភាព។',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'km', {
  6 + alt: 'អត្ថបទជំនួស',
  7 + btnUpload: 'បញ្ជូនទៅកាន់ម៉ាស៊ីនផ្តល់សេវា',
  8 + captioned: 'រូប​ដែល​មាន​ចំណង​ជើង',
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'ពត៌មានអំពីរូបភាព',
  11 + lockRatio: 'ចាក់​សោ​ផល​ធៀប',
  12 + menu: 'លក្ខណៈ​សម្បត្តិ​រូប​ភាព',
  13 + pathName: 'រូបភាព',
  14 + pathNameCaption: 'ចំណងជើង',
  15 + resetSize: 'កំណត់ទំហំឡើងវិញ',
  16 + resizer: 'ចុច​ហើយ​ទាញ​ដើម្បី​ប្ដូរ​ទំហំ',
  17 + title: 'លក្ខណៈ​សម្បត្តិ​រូប​ភាប',
  18 + uploadTab: 'ផ្ទុក​ឡើង',
  19 + urlMissing: 'ខ្វះ URL ប្រភព​រូប​ភាព។',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ko', {
  6 + alt: '대체 문자열',
  7 + btnUpload: '서버로 전송',
  8 + captioned: '이미지 설명 넣기',
  9 + captionPlaceholder: '설명',
  10 + infoTab: '이미지 정보',
  11 + lockRatio: '비율 유지',
  12 + menu: '이미지 속성',
  13 + pathName: '이미지',
  14 + pathNameCaption: '설명',
  15 + resetSize: '원래 크기로',
  16 + resizer: '크기를 조절하려면 클릭 후 드래그 하세요',
  17 + title: '이미지 속성',
  18 + uploadTab: '업로드',
  19 + urlMissing: '이미지 원본 주소(URL)가 없습니다.',
  20 + altMissing: '대체 문자가 없습니다.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ko', {
  6 + alt: '대체 문자열',
  7 + btnUpload: '서버로 전송',
  8 + captioned: '이미지 설명 넣기',
  9 + captionPlaceholder: '설명',
  10 + infoTab: '이미지 정보',
  11 + lockRatio: '비율 유지',
  12 + menu: '이미지 속성',
  13 + pathName: '이미지',
  14 + pathNameCaption: '설명',
  15 + resetSize: '원래 크기로',
  16 + resizer: '크기를 조절하려면 클릭 후 드래그 하세요',
  17 + title: '이미지 속성',
  18 + uploadTab: '업로드',
  19 + urlMissing: '이미지 원본 주소(URL)가 없습니다.',
  20 + altMissing: '대체 문자가 없습니다.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ku', {
  6 + alt: 'جێگرەوەی دەق',
  7 + btnUpload: 'ناردنی بۆ ڕاژه',
  8 + captioned: 'وێنەی بەسەردێر',
  9 + captionPlaceholder: 'سەردێر',
  10 + infoTab: 'زانیاری وێنه',
  11 + lockRatio: 'داخستنی ڕێژه',
  12 + menu: 'خاسیەتی وێنه',
  13 + pathName: 'وێنە',
  14 + pathNameCaption: 'سەردێر',
  15 + resetSize: 'ڕێکخستنەوەی قەباره',
  16 + resizer: 'کرتەبکە و ڕایبکێشە بۆ قەبارە گۆڕین',
  17 + title: 'خاسیەتی وێنه',
  18 + uploadTab: 'بارکردن',
  19 + urlMissing: 'سەرچاوەی بەستەری وێنه بزره',
  20 + altMissing: 'جێگرەوەی دەق لەدەست چووە.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ku', {
  6 + alt: 'جێگرەوەی دەق',
  7 + btnUpload: 'ناردنی بۆ ڕاژه',
  8 + captioned: 'وێنەی بەسەردێر',
  9 + captionPlaceholder: 'سەردێر',
  10 + infoTab: 'زانیاری وێنه',
  11 + lockRatio: 'داخستنی ڕێژه',
  12 + menu: 'خاسیەتی وێنه',
  13 + pathName: 'وێنە',
  14 + pathNameCaption: 'سەردێر',
  15 + resetSize: 'ڕێکخستنەوەی قەباره',
  16 + resizer: 'کرتەبکە و ڕایبکێشە بۆ قەبارە گۆڕین',
  17 + title: 'خاسیەتی وێنه',
  18 + uploadTab: 'بارکردن',
  19 + urlMissing: 'سەرچاوەی بەستەری وێنه بزره',
  20 + altMissing: 'جێگرەوەی دەق لەدەست چووە.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'lt', {
  6 + alt: 'Alternatyvus Tekstas',
  7 + btnUpload: 'Siųsti į serverį',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Vaizdo informacija',
  11 + lockRatio: 'Išlaikyti proporciją',
  12 + menu: 'Vaizdo savybės',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Atstatyti dydį',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Vaizdo savybės',
  18 + uploadTab: 'Siųsti',
  19 + urlMissing: 'Paveiksliuko nuorodos nėra.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'lt', {
  6 + alt: 'Alternatyvus Tekstas',
  7 + btnUpload: 'Siųsti į serverį',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Vaizdo informacija',
  11 + lockRatio: 'Išlaikyti proporciją',
  12 + menu: 'Vaizdo savybės',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Atstatyti dydį',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Vaizdo savybės',
  18 + uploadTab: 'Siųsti',
  19 + urlMissing: 'Paveiksliuko nuorodos nėra.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'lv', {
  6 + alt: 'Alternatīvais teksts',
  7 + btnUpload: 'Nosūtīt serverim',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Informācija par attēlu',
  11 + lockRatio: 'Nemainīga Augstuma/Platuma attiecība',
  12 + menu: 'Attēla īpašības',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Atjaunot sākotnējo izmēru',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Attēla īpašības',
  18 + uploadTab: 'Augšupielādēt',
  19 + urlMissing: 'Trūkst attēla atrašanās adrese.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'lv', {
  6 + alt: 'Alternatīvais teksts',
  7 + btnUpload: 'Nosūtīt serverim',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Informācija par attēlu',
  11 + lockRatio: 'Nemainīga Augstuma/Platuma attiecība',
  12 + menu: 'Attēla īpašības',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Atjaunot sākotnējo izmēru',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Attēla īpašības',
  18 + uploadTab: 'Augšupielādēt',
  19 + urlMissing: 'Trūkst attēla atrašanās adrese.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'mk', {
  6 + alt: 'Алтернативен текст',
  7 + btnUpload: 'Прикачи на сервер',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Информации за сликата',
  11 + lockRatio: 'Зачувај пропорција',
  12 + menu: 'Својства на сликата',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Ресетирај големина',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Својства на сликата',
  18 + uploadTab: 'Прикачи',
  19 + urlMissing: 'Недостасува URL-то на сликата.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'mk', {
  6 + alt: 'Алтернативен текст',
  7 + btnUpload: 'Прикачи на сервер',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Информации за сликата',
  11 + lockRatio: 'Зачувај пропорција',
  12 + menu: 'Својства на сликата',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Ресетирај големина',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Својства на сликата',
  18 + uploadTab: 'Прикачи',
  19 + urlMissing: 'Недостасува URL-то на сликата.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'mn', {
  6 + alt: 'Зургийг орлох бичвэр',
  7 + btnUpload: 'Үүнийг сервэррүү илгээ',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Зурагны мэдээлэл',
  11 + lockRatio: 'Радио түгжих',
  12 + menu: 'Зураг',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'хэмжээ дахин оноох',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Зураг',
  18 + uploadTab: 'Илгээж ачаалах',
  19 + urlMissing: 'Зургийн эх сурвалжийн хаяг (URL) байхгүй байна.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'mn', {
  6 + alt: 'Зургийг орлох бичвэр',
  7 + btnUpload: 'Үүнийг сервэррүү илгээ',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Зурагны мэдээлэл',
  11 + lockRatio: 'Радио түгжих',
  12 + menu: 'Зураг',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'хэмжээ дахин оноох',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Зураг',
  18 + uploadTab: 'Илгээж ачаалах',
  19 + urlMissing: 'Зургийн эх сурвалжийн хаяг (URL) байхгүй байна.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ms', {
  6 + alt: 'Text Alternatif',
  7 + btnUpload: 'Hantar ke Server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Info Imej',
  11 + lockRatio: 'Tetapkan Nisbah',
  12 + menu: 'Ciri-ciri Imej',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Saiz Set Semula',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Ciri-ciri Imej',
  18 + uploadTab: 'Muat Naik',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ms', {
  6 + alt: 'Text Alternatif',
  7 + btnUpload: 'Hantar ke Server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Info Imej',
  11 + lockRatio: 'Tetapkan Nisbah',
  12 + menu: 'Ciri-ciri Imej',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Saiz Set Semula',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Ciri-ciri Imej',
  18 + uploadTab: 'Muat Naik',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'nb', {
  6 + alt: 'Alternativ tekst',
  7 + btnUpload: 'Send det til serveren',
  8 + captioned: 'Bilde med bildetekst',
  9 + captionPlaceholder: 'Bildetekst',
  10 + infoTab: 'Bildeinformasjon',
  11 + lockRatio: 'Lås forhold',
  12 + menu: 'Bildeegenskaper',
  13 + pathName: 'bilde',
  14 + pathNameCaption: 'bildetekst',
  15 + resetSize: 'Tilbakestill størrelse',
  16 + resizer: 'Klikk og dra for å endre størrelse',
  17 + title: 'Bildeegenskaper',
  18 + uploadTab: 'Last opp',
  19 + urlMissing: 'Bildets adresse mangler.',
  20 + altMissing: 'Alternativ tekst mangler.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'nb', {
  6 + alt: 'Alternativ tekst',
  7 + btnUpload: 'Send det til serveren',
  8 + captioned: 'Bilde med bildetekst',
  9 + captionPlaceholder: 'Bildetekst',
  10 + infoTab: 'Bildeinformasjon',
  11 + lockRatio: 'Lås forhold',
  12 + menu: 'Bildeegenskaper',
  13 + pathName: 'bilde',
  14 + pathNameCaption: 'bildetekst',
  15 + resetSize: 'Tilbakestill størrelse',
  16 + resizer: 'Klikk og dra for å endre størrelse',
  17 + title: 'Bildeegenskaper',
  18 + uploadTab: 'Last opp',
  19 + urlMissing: 'Bildets adresse mangler.',
  20 + altMissing: 'Alternativ tekst mangler.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'nl', {
  6 + alt: 'Alternatieve tekst',
  7 + btnUpload: 'Naar server verzenden',
  8 + captioned: 'Afbeelding met onderschrift',
  9 + captionPlaceholder: 'Onderschrift',
  10 + infoTab: 'Afbeeldingsinformatie',
  11 + lockRatio: 'Verhouding vergrendelen',
  12 + menu: 'Eigenschappen afbeelding',
  13 + pathName: 'afbeelding',
  14 + pathNameCaption: 'onderschrift',
  15 + resetSize: 'Afmetingen herstellen',
  16 + resizer: 'Klik en sleep om te herschalen',
  17 + title: 'Afbeeldingseigenschappen',
  18 + uploadTab: 'Uploaden',
  19 + urlMissing: 'De URL naar de afbeelding ontbreekt.',
  20 + altMissing: 'Alternatieve tekst ontbreekt.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'nl', {
  6 + alt: 'Alternatieve tekst',
  7 + btnUpload: 'Naar server verzenden',
  8 + captioned: 'Afbeelding met onderschrift',
  9 + captionPlaceholder: 'Onderschrift',
  10 + infoTab: 'Afbeeldingsinformatie',
  11 + lockRatio: 'Verhouding vergrendelen',
  12 + menu: 'Eigenschappen afbeelding',
  13 + pathName: 'afbeelding',
  14 + pathNameCaption: 'onderschrift',
  15 + resetSize: 'Afmetingen herstellen',
  16 + resizer: 'Klik en sleep om te herschalen',
  17 + title: 'Afbeeldingseigenschappen',
  18 + uploadTab: 'Uploaden',
  19 + urlMissing: 'De URL naar de afbeelding ontbreekt.',
  20 + altMissing: 'Alternatieve tekst ontbreekt.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'no', {
  6 + alt: 'Alternativ tekst',
  7 + btnUpload: 'Send det til serveren',
  8 + captioned: 'Bilde med bildetekst',
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Bildeinformasjon',
  11 + lockRatio: 'Lås forhold',
  12 + menu: 'Bildeegenskaper',
  13 + pathName: 'bilde',
  14 + pathNameCaption: 'bildetekst',
  15 + resetSize: 'Tilbakestill størrelse',
  16 + resizer: 'Klikk og dra for å endre størrelse',
  17 + title: 'Bildeegenskaper',
  18 + uploadTab: 'Last opp',
  19 + urlMissing: 'Bildets adresse mangler.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'no', {
  6 + alt: 'Alternativ tekst',
  7 + btnUpload: 'Send det til serveren',
  8 + captioned: 'Bilde med bildetekst',
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Bildeinformasjon',
  11 + lockRatio: 'Lås forhold',
  12 + menu: 'Bildeegenskaper',
  13 + pathName: 'bilde',
  14 + pathNameCaption: 'bildetekst',
  15 + resetSize: 'Tilbakestill størrelse',
  16 + resizer: 'Klikk og dra for å endre størrelse',
  17 + title: 'Bildeegenskaper',
  18 + uploadTab: 'Last opp',
  19 + urlMissing: 'Bildets adresse mangler.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'oc', {
  6 + alt: 'Tèxte alternatiu',
  7 + btnUpload: 'Mandar sul servidor',
  8 + captioned: 'Imatge amb legenda',
  9 + captionPlaceholder: 'Legenda',
  10 + infoTab: 'Informacions sus l\'imatge',
  11 + lockRatio: 'Conservar las proporcions',
  12 + menu: 'Proprietats de l\'imatge',
  13 + pathName: 'imatge',
  14 + pathNameCaption: 'legenda',
  15 + resetSize: 'Reïnicializar la talha',
  16 + resizer: 'Clicar e lisar per redimensionar',
  17 + title: 'Proprietats de l\'imatge',
  18 + uploadTab: 'Mandar',
  19 + urlMissing: 'L\'URL font de l\'imatge es mancanta.',
  20 + altMissing: 'Lo tèxte alternatiu es mancant.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'oc', {
  6 + alt: 'Tèxte alternatiu',
  7 + btnUpload: 'Mandar sul servidor',
  8 + captioned: 'Imatge amb legenda',
  9 + captionPlaceholder: 'Legenda',
  10 + infoTab: 'Informacions sus l\'imatge',
  11 + lockRatio: 'Conservar las proporcions',
  12 + menu: 'Proprietats de l\'imatge',
  13 + pathName: 'imatge',
  14 + pathNameCaption: 'legenda',
  15 + resetSize: 'Reïnicializar la talha',
  16 + resizer: 'Clicar e lisar per redimensionar',
  17 + title: 'Proprietats de l\'imatge',
  18 + uploadTab: 'Mandar',
  19 + urlMissing: 'L\'URL font de l\'imatge es mancanta.',
  20 + altMissing: 'Lo tèxte alternatiu es mancant.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'pl', {
  6 + alt: 'Tekst zastępczy',
  7 + btnUpload: 'Wyślij',
  8 + captioned: 'Obrazek z podpisem',
  9 + captionPlaceholder: 'Podpis',
  10 + infoTab: 'Informacje o obrazku',
  11 + lockRatio: 'Zablokuj proporcje',
  12 + menu: 'Właściwości obrazka',
  13 + pathName: 'obrazek',
  14 + pathNameCaption: 'podpis',
  15 + resetSize: 'Przywróć rozmiar',
  16 + resizer: 'Kliknij i przeciągnij, by zmienić rozmiar.',
  17 + title: 'Właściwości obrazka',
  18 + uploadTab: 'Wyślij',
  19 + urlMissing: 'Podaj adres URL obrazka.',
  20 + altMissing: 'Podaj tekst zastępczy obrazka.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'pl', {
  6 + alt: 'Tekst zastępczy',
  7 + btnUpload: 'Wyślij',
  8 + captioned: 'Obrazek z podpisem',
  9 + captionPlaceholder: 'Podpis',
  10 + infoTab: 'Informacje o obrazku',
  11 + lockRatio: 'Zablokuj proporcje',
  12 + menu: 'Właściwości obrazka',
  13 + pathName: 'obrazek',
  14 + pathNameCaption: 'podpis',
  15 + resetSize: 'Przywróć rozmiar',
  16 + resizer: 'Kliknij i przeciągnij, by zmienić rozmiar.',
  17 + title: 'Właściwości obrazka',
  18 + uploadTab: 'Wyślij',
  19 + urlMissing: 'Podaj adres URL obrazka.',
  20 + altMissing: 'Podaj tekst zastępczy obrazka.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'pt-br', {
  6 + alt: 'Texto Alternativo',
  7 + btnUpload: 'Enviar para o Servidor',
  8 + captioned: 'Legenda da Imagem',
  9 + captionPlaceholder: 'Legenda',
  10 + infoTab: 'Informações da Imagem',
  11 + lockRatio: 'Travar Proporções',
  12 + menu: 'Formatar Imagem',
  13 + pathName: 'Imagem',
  14 + pathNameCaption: 'Legenda',
  15 + resetSize: 'Redefinir para o Tamanho Original',
  16 + resizer: 'Click e arraste para redimensionar',
  17 + title: 'Formatar Imagem',
  18 + uploadTab: 'Enviar ao Servidor',
  19 + urlMissing: 'URL da imagem está faltando.',
  20 + altMissing: 'Texto alternativo não informado.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'pt-br', {
  6 + alt: 'Texto Alternativo',
  7 + btnUpload: 'Enviar para o Servidor',
  8 + captioned: 'Legenda da Imagem',
  9 + captionPlaceholder: 'Legenda',
  10 + infoTab: 'Informações da Imagem',
  11 + lockRatio: 'Travar Proporções',
  12 + menu: 'Formatar Imagem',
  13 + pathName: 'Imagem',
  14 + pathNameCaption: 'Legenda',
  15 + resetSize: 'Redefinir para o Tamanho Original',
  16 + resizer: 'Click e arraste para redimensionar',
  17 + title: 'Formatar Imagem',
  18 + uploadTab: 'Enviar ao Servidor',
  19 + urlMissing: 'URL da imagem está faltando.',
  20 + altMissing: 'Texto alternativo não informado.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'pt', {
  6 + alt: 'Texto alternativo',
  7 + btnUpload: 'Enviar para o servidor',
  8 + captioned: 'Imagem legendada',
  9 + captionPlaceholder: 'Legenda',
  10 + infoTab: 'Informação da imagem',
  11 + lockRatio: 'Proporcional',
  12 + menu: 'Propriedades da imagem',
  13 + pathName: 'imagem',
  14 + pathNameCaption: 'legenda',
  15 + resetSize: 'Tamanho original',
  16 + resizer: 'Clique e arraste para redimensionar',
  17 + title: 'Propriedades da imagem',
  18 + uploadTab: 'Carregar',
  19 + urlMissing: 'O URL de origem da imagem está em falta.',
  20 + altMissing: 'Texto alternativo em falta.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'pt', {
  6 + alt: 'Texto alternativo',
  7 + btnUpload: 'Enviar para o servidor',
  8 + captioned: 'Imagem legendada',
  9 + captionPlaceholder: 'Legenda',
  10 + infoTab: 'Informação da imagem',
  11 + lockRatio: 'Proporcional',
  12 + menu: 'Propriedades da imagem',
  13 + pathName: 'imagem',
  14 + pathNameCaption: 'legenda',
  15 + resetSize: 'Tamanho original',
  16 + resizer: 'Clique e arraste para redimensionar',
  17 + title: 'Propriedades da imagem',
  18 + uploadTab: 'Carregar',
  19 + urlMissing: 'O URL de origem da imagem está em falta.',
  20 + altMissing: 'Texto alternativo em falta.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ro', {
  6 + alt: 'Text alternativ',
  7 + btnUpload: 'Trimite la server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Informaţii despre imagine',
  11 + lockRatio: 'Păstrează proporţiile',
  12 + menu: 'Proprietăţile imaginii',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Resetează mărimea',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Proprietăţile imaginii',
  18 + uploadTab: 'Încarcă',
  19 + urlMissing: 'Sursa URL a imaginii lipsește.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ro', {
  6 + alt: 'Text alternativ',
  7 + btnUpload: 'Trimite la server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Informaţii despre imagine',
  11 + lockRatio: 'Păstrează proporţiile',
  12 + menu: 'Proprietăţile imaginii',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Resetează mărimea',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Proprietăţile imaginii',
  18 + uploadTab: 'Încarcă',
  19 + urlMissing: 'Sursa URL a imaginii lipsește.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ru', {
  6 + alt: 'Альтернативный текст',
  7 + btnUpload: 'Загрузить на сервер',
  8 + captioned: 'Отображать название',
  9 + captionPlaceholder: 'Название',
  10 + infoTab: 'Данные об изображении',
  11 + lockRatio: 'Сохранять пропорции',
  12 + menu: 'Свойства изображения',
  13 + pathName: 'изображение',
  14 + pathNameCaption: 'название',
  15 + resetSize: 'Вернуть обычные размеры',
  16 + resizer: 'Нажмите и растяните',
  17 + title: 'Свойства изображения',
  18 + uploadTab: 'Загрузка файла',
  19 + urlMissing: 'Не указана ссылка на изображение.',
  20 + altMissing: 'Не задан альтернативный текст'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ru', {
  6 + alt: 'Альтернативный текст',
  7 + btnUpload: 'Загрузить на сервер',
  8 + captioned: 'Отображать название',
  9 + captionPlaceholder: 'Название',
  10 + infoTab: 'Данные об изображении',
  11 + lockRatio: 'Сохранять пропорции',
  12 + menu: 'Свойства изображения',
  13 + pathName: 'изображение',
  14 + pathNameCaption: 'название',
  15 + resetSize: 'Вернуть обычные размеры',
  16 + resizer: 'Нажмите и растяните',
  17 + title: 'Свойства изображения',
  18 + uploadTab: 'Загрузка файла',
  19 + urlMissing: 'Не указана ссылка на изображение.',
  20 + altMissing: 'Не задан альтернативный текст'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'si', {
  6 + alt: 'විකල්ප ',
  7 + btnUpload: 'සේවාදායකය වෙත යොමුකිරිම',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'රුපයේ තොරතුරු',
  11 + lockRatio: 'නවතන අනුපාතය ',
  12 + menu: 'රුපයේ ගුණ',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'නැවතත් විශාලත්වය වෙනස් කිරීම',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'රුපයේ ',
  18 + uploadTab: 'උඩුගතකිරීම',
  19 + urlMissing: 'රුප මුලාශ්‍ර URL නැත.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'si', {
  6 + alt: 'විකල්ප ',
  7 + btnUpload: 'සේවාදායකය වෙත යොමුකිරිම',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'රුපයේ තොරතුරු',
  11 + lockRatio: 'නවතන අනුපාතය ',
  12 + menu: 'රුපයේ ගුණ',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'නැවතත් විශාලත්වය වෙනස් කිරීම',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'රුපයේ ',
  18 + uploadTab: 'උඩුගතකිරීම',
  19 + urlMissing: 'රුප මුලාශ්‍ර URL නැත.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sk', {
  6 + alt: 'Alternatívny text',
  7 + btnUpload: 'Odoslať to na server',
  8 + captioned: 'Opísaný obrázok',
  9 + captionPlaceholder: 'Popis',
  10 + infoTab: 'Informácie o obrázku',
  11 + lockRatio: 'Pomer zámky',
  12 + menu: 'Vlastnosti obrázka',
  13 + pathName: 'obrázok',
  14 + pathNameCaption: 'popis',
  15 + resetSize: 'Pôvodná veľkosť',
  16 + resizer: 'Kliknite a potiahnite pre zmenu veľkosti',
  17 + title: 'Vlastnosti obrázka',
  18 + uploadTab: 'Nahrať',
  19 + urlMissing: 'Chýba URL zdroja obrázka.',
  20 + altMissing: 'Chýba alternatívny text.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sk', {
  6 + alt: 'Alternatívny text',
  7 + btnUpload: 'Odoslať to na server',
  8 + captioned: 'Opísaný obrázok',
  9 + captionPlaceholder: 'Popis',
  10 + infoTab: 'Informácie o obrázku',
  11 + lockRatio: 'Pomer zámky',
  12 + menu: 'Vlastnosti obrázka',
  13 + pathName: 'obrázok',
  14 + pathNameCaption: 'popis',
  15 + resetSize: 'Pôvodná veľkosť',
  16 + resizer: 'Kliknite a potiahnite pre zmenu veľkosti',
  17 + title: 'Vlastnosti obrázka',
  18 + uploadTab: 'Nahrať',
  19 + urlMissing: 'Chýba URL zdroja obrázka.',
  20 + altMissing: 'Chýba alternatívny text.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sl', {
  6 + alt: 'Nadomestno besedilo',
  7 + btnUpload: 'Pošlji na strežnik',
  8 + captioned: 'Slika z napisom',
  9 + captionPlaceholder: 'Napis',
  10 + infoTab: 'Podatki o sliki',
  11 + lockRatio: 'Zakleni razmerje',
  12 + menu: 'Lastnosti slike',
  13 + pathName: 'slika',
  14 + pathNameCaption: 'napis',
  15 + resetSize: 'Ponastavi velikost',
  16 + resizer: 'Kliknite in povlecite, da spremenite velikost',
  17 + title: 'Lastnosti slike',
  18 + uploadTab: 'Naloži',
  19 + urlMissing: 'Manjka vir (URL) slike.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sl', {
  6 + alt: 'Nadomestno besedilo',
  7 + btnUpload: 'Pošlji na strežnik',
  8 + captioned: 'Slika z napisom',
  9 + captionPlaceholder: 'Napis',
  10 + infoTab: 'Podatki o sliki',
  11 + lockRatio: 'Zakleni razmerje',
  12 + menu: 'Lastnosti slike',
  13 + pathName: 'slika',
  14 + pathNameCaption: 'napis',
  15 + resetSize: 'Ponastavi velikost',
  16 + resizer: 'Kliknite in povlecite, da spremenite velikost',
  17 + title: 'Lastnosti slike',
  18 + uploadTab: 'Naloži',
  19 + urlMissing: 'Manjka vir (URL) slike.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sq', {
  6 + alt: 'Tekst Alternativ',
  7 + btnUpload: 'Dërgo në server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Informacione mbi Fotografinë',
  11 + lockRatio: 'Mbyll Racionin',
  12 + menu: 'Karakteristikat e Fotografisë',
  13 + pathName: 'foto',
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Rikthe Madhësinë',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Karakteristikat e Fotografisë',
  18 + uploadTab: 'Ngarko',
  19 + urlMissing: 'Mungon URL e burimit të fotografisë.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sq', {
  6 + alt: 'Tekst Alternativ',
  7 + btnUpload: 'Dërgo në server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Informacione mbi Fotografinë',
  11 + lockRatio: 'Mbyll Racionin',
  12 + menu: 'Karakteristikat e Fotografisë',
  13 + pathName: 'foto',
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Rikthe Madhësinë',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Karakteristikat e Fotografisë',
  18 + uploadTab: 'Ngarko',
  19 + urlMissing: 'Mungon URL e burimit të fotografisë.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sr-latn', {
  6 + alt: 'Alternativni tekst',
  7 + btnUpload: 'Pošalji na server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Info slike',
  11 + lockRatio: 'Zaključaj odnos',
  12 + menu: 'Osobine slika',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Resetuj veličinu',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Osobine slika',
  18 + uploadTab: 'Pošalji',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sr-latn', {
  6 + alt: 'Alternativni tekst',
  7 + btnUpload: 'Pošalji na server',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Info slike',
  11 + lockRatio: 'Zaključaj odnos',
  12 + menu: 'Osobine slika',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Resetuj veličinu',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Osobine slika',
  18 + uploadTab: 'Pošalji',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sr', {
  6 + alt: 'Алтернативни текст',
  7 + btnUpload: 'Пошаљи на сервер',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Инфо слике',
  11 + lockRatio: 'Закључај однос',
  12 + menu: 'Особине слика',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Ресетуј величину',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Особине слика',
  18 + uploadTab: 'Пошаљи',
  19 + urlMissing: 'Недостаје УРЛ слике.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sr', {
  6 + alt: 'Алтернативни текст',
  7 + btnUpload: 'Пошаљи на сервер',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'Инфо слике',
  11 + lockRatio: 'Закључај однос',
  12 + menu: 'Особине слика',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'Ресетуј величину',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'Особине слика',
  18 + uploadTab: 'Пошаљи',
  19 + urlMissing: 'Недостаје УРЛ слике.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sv', {
  6 + alt: 'Alternativ text',
  7 + btnUpload: 'Skicka till server',
  8 + captioned: 'Rubricerad bild',
  9 + captionPlaceholder: 'Bildtext',
  10 + infoTab: 'Bildinformation',
  11 + lockRatio: 'Lås höjd/bredd förhållanden',
  12 + menu: 'Bildegenskaper',
  13 + pathName: 'bild',
  14 + pathNameCaption: 'rubrik',
  15 + resetSize: 'Återställ storlek',
  16 + resizer: 'Klicka och drag för att ändra storlek',
  17 + title: 'Bildegenskaper',
  18 + uploadTab: 'Ladda upp',
  19 + urlMissing: 'Bildkällans URL saknas.',
  20 + altMissing: 'Alternativ text saknas'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'sv', {
  6 + alt: 'Alternativ text',
  7 + btnUpload: 'Skicka till server',
  8 + captioned: 'Rubricerad bild',
  9 + captionPlaceholder: 'Bildtext',
  10 + infoTab: 'Bildinformation',
  11 + lockRatio: 'Lås höjd/bredd förhållanden',
  12 + menu: 'Bildegenskaper',
  13 + pathName: 'bild',
  14 + pathNameCaption: 'rubrik',
  15 + resetSize: 'Återställ storlek',
  16 + resizer: 'Klicka och drag för att ändra storlek',
  17 + title: 'Bildegenskaper',
  18 + uploadTab: 'Ladda upp',
  19 + urlMissing: 'Bildkällans URL saknas.',
  20 + altMissing: 'Alternativ text saknas'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'th', {
  6 + alt: 'คำประกอบรูปภาพ',
  7 + btnUpload: 'อัพโหลดไฟล์ไปเก็บไว้ที่เครื่องแม่ข่าย (เซิร์ฟเวอร์)',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'ข้อมูลของรูปภาพ',
  11 + lockRatio: 'กำหนดอัตราส่วน กว้าง-สูง แบบคงที่',
  12 + menu: 'คุณสมบัติของ รูปภาพ',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'กำหนดรูปเท่าขนาดจริง',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'คุณสมบัติของ รูปภาพ',
  18 + uploadTab: 'อัพโหลดไฟล์',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'th', {
  6 + alt: 'คำประกอบรูปภาพ',
  7 + btnUpload: 'อัพโหลดไฟล์ไปเก็บไว้ที่เครื่องแม่ข่าย (เซิร์ฟเวอร์)',
  8 + captioned: 'Captioned image', // MISSING
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'ข้อมูลของรูปภาพ',
  11 + lockRatio: 'กำหนดอัตราส่วน กว้าง-สูง แบบคงที่',
  12 + menu: 'คุณสมบัติของ รูปภาพ',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'กำหนดรูปเท่าขนาดจริง',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'คุณสมบัติของ รูปภาพ',
  18 + uploadTab: 'อัพโหลดไฟล์',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'tr', {
  6 + alt: 'Alternatif Yazı',
  7 + btnUpload: 'Sunucuya Yolla',
  8 + captioned: 'Başlıklı resim',
  9 + captionPlaceholder: 'Başlık',
  10 + infoTab: 'Resim Bilgisi',
  11 + lockRatio: 'Oranı Kilitle',
  12 + menu: 'Resim Özellikleri',
  13 + pathName: 'Resim',
  14 + pathNameCaption: 'başlık',
  15 + resetSize: 'Boyutu Başa Döndür',
  16 + resizer: 'Boyutlandırmak için, tıklayın ve sürükleyin',
  17 + title: 'Resim Özellikleri',
  18 + uploadTab: 'Karşıya Yükle',
  19 + urlMissing: 'Resmin URL kaynağı bulunamadı.',
  20 + altMissing: 'Alternatif yazı eksik.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'tr', {
  6 + alt: 'Alternatif Yazı',
  7 + btnUpload: 'Sunucuya Yolla',
  8 + captioned: 'Başlıklı resim',
  9 + captionPlaceholder: 'Başlık',
  10 + infoTab: 'Resim Bilgisi',
  11 + lockRatio: 'Oranı Kilitle',
  12 + menu: 'Resim Özellikleri',
  13 + pathName: 'Resim',
  14 + pathNameCaption: 'başlık',
  15 + resetSize: 'Boyutu Başa Döndür',
  16 + resizer: 'Boyutlandırmak için, tıklayın ve sürükleyin',
  17 + title: 'Resim Özellikleri',
  18 + uploadTab: 'Karşıya Yükle',
  19 + urlMissing: 'Resmin URL kaynağı bulunamadı.',
  20 + altMissing: 'Alternatif yazı eksik.'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'tt', {
  6 + alt: 'Альтернатив текст',
  7 + btnUpload: 'Серверга җибәрү',
  8 + captioned: 'Исеме куелган рәсем',
  9 + captionPlaceholder: 'Исем',
  10 + infoTab: 'Рәсем тасвирламасы',
  11 + lockRatio: 'Lock Ratio', // MISSING
  12 + menu: 'Рәсем үзлекләре',
  13 + pathName: 'рәсем',
  14 + pathNameCaption: 'исем',
  15 + resetSize: 'Баштагы зурлык',
  16 + resizer: 'Күчереп куер өчен басып шудырыгыз',
  17 + title: 'Рәсем үзлекләре',
  18 + uploadTab: 'Йөкләү',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'tt', {
  6 + alt: 'Альтернатив текст',
  7 + btnUpload: 'Серверга җибәрү',
  8 + captioned: 'Исеме куелган рәсем',
  9 + captionPlaceholder: 'Исем',
  10 + infoTab: 'Рәсем тасвирламасы',
  11 + lockRatio: 'Lock Ratio', // MISSING
  12 + menu: 'Рәсем үзлекләре',
  13 + pathName: 'рәсем',
  14 + pathNameCaption: 'исем',
  15 + resetSize: 'Баштагы зурлык',
  16 + resizer: 'Күчереп куер өчен басып шудырыгыз',
  17 + title: 'Рәсем үзлекләре',
  18 + uploadTab: 'Йөкләү',
  19 + urlMissing: 'Image source URL is missing.', // MISSING
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ug', {
  6 + alt: 'تېكىست ئالماشتۇر',
  7 + btnUpload: 'مۇلازىمېتىرغا يۈكلە',
  8 + captioned: 'ماۋزۇلۇق سۈرەت',
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'سۈرەت',
  11 + lockRatio: 'نىسبەتنى قۇلۇپلا',
  12 + menu: 'سۈرەت خاسلىقى',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'ئەسلى چوڭلۇق',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'سۈرەت خاسلىقى',
  18 + uploadTab: 'يۈكلە',
  19 + urlMissing: 'سۈرەتنىڭ ئەسلى ھۆججەت ئادرېسى كەم',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'ug', {
  6 + alt: 'تېكىست ئالماشتۇر',
  7 + btnUpload: 'مۇلازىمېتىرغا يۈكلە',
  8 + captioned: 'ماۋزۇلۇق سۈرەت',
  9 + captionPlaceholder: 'Caption', // MISSING
  10 + infoTab: 'سۈرەت',
  11 + lockRatio: 'نىسبەتنى قۇلۇپلا',
  12 + menu: 'سۈرەت خاسلىقى',
  13 + pathName: 'image', // MISSING
  14 + pathNameCaption: 'caption', // MISSING
  15 + resetSize: 'ئەسلى چوڭلۇق',
  16 + resizer: 'Click and drag to resize', // MISSING
  17 + title: 'سۈرەت خاسلىقى',
  18 + uploadTab: 'يۈكلە',
  19 + urlMissing: 'سۈرەتنىڭ ئەسلى ھۆججەت ئادرېسى كەم',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'uk', {
  6 + alt: 'Альтернативний текст',
  7 + btnUpload: 'Надіслати на сервер',
  8 + captioned: 'Підписане зображення',
  9 + captionPlaceholder: 'Заголовок',
  10 + infoTab: 'Інформація про зображення',
  11 + lockRatio: 'Зберегти пропорції',
  12 + menu: 'Властивості зображення',
  13 + pathName: 'Зображення',
  14 + pathNameCaption: 'заголовок',
  15 + resetSize: 'Очистити поля розмірів',
  16 + resizer: 'Клікніть та потягніть для зміни розмірів',
  17 + title: 'Властивості зображення',
  18 + uploadTab: 'Надіслати',
  19 + urlMissing: 'Вкажіть URL зображення.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'uk', {
  6 + alt: 'Альтернативний текст',
  7 + btnUpload: 'Надіслати на сервер',
  8 + captioned: 'Підписане зображення',
  9 + captionPlaceholder: 'Заголовок',
  10 + infoTab: 'Інформація про зображення',
  11 + lockRatio: 'Зберегти пропорції',
  12 + menu: 'Властивості зображення',
  13 + pathName: 'Зображення',
  14 + pathNameCaption: 'заголовок',
  15 + resetSize: 'Очистити поля розмірів',
  16 + resizer: 'Клікніть та потягніть для зміни розмірів',
  17 + title: 'Властивості зображення',
  18 + uploadTab: 'Надіслати',
  19 + urlMissing: 'Вкажіть URL зображення.',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'vi', {
  6 + alt: 'Chú thích ảnh',
  7 + btnUpload: 'Tải lên máy chủ',
  8 + captioned: 'Ảnh có chú thích',
  9 + captionPlaceholder: 'Nhãn',
  10 + infoTab: 'Thông tin của ảnh',
  11 + lockRatio: 'Giữ nguyên tỷ lệ',
  12 + menu: 'Thuộc tính của ảnh',
  13 + pathName: 'ảnh',
  14 + pathNameCaption: 'chú thích',
  15 + resetSize: 'Kích thước gốc',
  16 + resizer: 'Kéo rê để thay đổi kích cỡ',
  17 + title: 'Thuộc tính của ảnh',
  18 + uploadTab: 'Tải lên',
  19 + urlMissing: 'Thiếu đường dẫn hình ảnh',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'vi', {
  6 + alt: 'Chú thích ảnh',
  7 + btnUpload: 'Tải lên máy chủ',
  8 + captioned: 'Ảnh có chú thích',
  9 + captionPlaceholder: 'Nhãn',
  10 + infoTab: 'Thông tin của ảnh',
  11 + lockRatio: 'Giữ nguyên tỷ lệ',
  12 + menu: 'Thuộc tính của ảnh',
  13 + pathName: 'ảnh',
  14 + pathNameCaption: 'chú thích',
  15 + resetSize: 'Kích thước gốc',
  16 + resizer: 'Kéo rê để thay đổi kích cỡ',
  17 + title: 'Thuộc tính của ảnh',
  18 + uploadTab: 'Tải lên',
  19 + urlMissing: 'Thiếu đường dẫn hình ảnh',
  20 + altMissing: 'Alternative text is missing.' // MISSING
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'zh-cn', {
  6 + alt: '替换文本',
  7 + btnUpload: '上传到服务器',
  8 + captioned: '带标题图像',
  9 + captionPlaceholder: '标题',
  10 + infoTab: '图像信息',
  11 + lockRatio: '锁定比例',
  12 + menu: '图像属性',
  13 + pathName: '图像',
  14 + pathNameCaption: '标题',
  15 + resetSize: '原始尺寸',
  16 + resizer: '点击并拖拽以改变尺寸',
  17 + title: '图像属性',
  18 + uploadTab: '上传',
  19 + urlMissing: '缺少图像源文件地址',
  20 + altMissing: '缺少替换文本'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'zh-cn', {
  6 + alt: '替换文本',
  7 + btnUpload: '上传到服务器',
  8 + captioned: '带标题图像',
  9 + captionPlaceholder: '标题',
  10 + infoTab: '图像信息',
  11 + lockRatio: '锁定比例',
  12 + menu: '图像属性',
  13 + pathName: '图像',
  14 + pathNameCaption: '标题',
  15 + resetSize: '原始尺寸',
  16 + resizer: '点击并拖拽以改变尺寸',
  17 + title: '图像属性',
  18 + uploadTab: '上传',
  19 + urlMissing: '缺少图像源文件地址',
  20 + altMissing: '缺少替换文本'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'zh', {
  6 + alt: '替代文字',
  7 + btnUpload: '傳送至伺服器',
  8 + captioned: '已加標題之圖片',
  9 + captionPlaceholder: '標題',
  10 + infoTab: '影像資訊',
  11 + lockRatio: '固定比例',
  12 + menu: '影像屬性',
  13 + pathName: '圖片',
  14 + pathNameCaption: '標題',
  15 + resetSize: '重設大小',
  16 + resizer: '拖曳以改變大小',
  17 + title: '影像屬性',
  18 + uploadTab: '上傳',
  19 + urlMissing: '遺失圖片來源之 URL ',
  20 + altMissing: '替代文字遺失。'
  21 +} );
  1 +/*
  2 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 +For licensing, see LICENSE.md or http://ckeditor.com/license
  4 +*/
  5 +CKEDITOR.plugins.setLang( 'image2', 'zh', {
  6 + alt: '替代文字',
  7 + btnUpload: '傳送至伺服器',
  8 + captioned: '已加標題之圖片',
  9 + captionPlaceholder: '標題',
  10 + infoTab: '影像資訊',
  11 + lockRatio: '固定比例',
  12 + menu: '影像屬性',
  13 + pathName: '圖片',
  14 + pathNameCaption: '標題',
  15 + resetSize: '重設大小',
  16 + resizer: '拖曳以改變大小',
  17 + title: '影像屬性',
  18 + uploadTab: '上傳',
  19 + urlMissing: '遺失圖片來源之 URL ',
  20 + altMissing: '替代文字遺失。'
  21 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +
  6 +'use strict';
  7 +
  8 +( function() {
  9 +
  10 + var template = '<img alt="" src="" />',
  11 + templateBlock = new CKEDITOR.template(
  12 + '<figure class="{captionedClass}">' +
  13 + template +
  14 + '<figcaption>{captionPlaceholder}</figcaption>' +
  15 + '</figure>' ),
  16 + alignmentsObj = { left: 0, center: 1, right: 2 },
  17 + regexPercent = /^\s*(\d+\%)\s*$/i;
  18 +
  19 + CKEDITOR.plugins.add( 'image2', {
  20 + // jscs:disable maximumLineLength
  21 + lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,es-mx,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
  22 + // jscs:enable maximumLineLength
  23 + requires: 'widget,dialog',
  24 + icons: 'image',
  25 + hidpi: true,
  26 +
  27 + onLoad: function() {
  28 + CKEDITOR.addCss(
  29 + '.cke_image_nocaption{' +
  30 + // This is to remove unwanted space so resize
  31 + // wrapper is displayed property.
  32 + 'line-height:0' +
  33 + '}' +
  34 + '.cke_editable.cke_image_sw, .cke_editable.cke_image_sw *{cursor:sw-resize !important}' +
  35 + '.cke_editable.cke_image_se, .cke_editable.cke_image_se *{cursor:se-resize !important}' +
  36 + '.cke_image_resizer{' +
  37 + 'display:none;' +
  38 + 'position:absolute;' +
  39 + 'width:10px;' +
  40 + 'height:10px;' +
  41 + 'bottom:-5px;' +
  42 + 'right:-5px;' +
  43 + 'background:#000;' +
  44 + 'outline:1px solid #fff;' +
  45 + // Prevent drag handler from being misplaced (http://dev.ckeditor.com/ticket/11207).
  46 + 'line-height:0;' +
  47 + 'cursor:se-resize;' +
  48 + '}' +
  49 + '.cke_image_resizer_wrapper{' +
  50 + 'position:relative;' +
  51 + 'display:inline-block;' +
  52 + 'line-height:0;' +
  53 + '}' +
  54 + // Bottom-left corner style of the resizer.
  55 + '.cke_image_resizer.cke_image_resizer_left{' +
  56 + 'right:auto;' +
  57 + 'left:-5px;' +
  58 + 'cursor:sw-resize;' +
  59 + '}' +
  60 + '.cke_widget_wrapper:hover .cke_image_resizer,' +
  61 + '.cke_image_resizer.cke_image_resizing{' +
  62 + 'display:block' +
  63 + '}' +
  64 + // Expand widget wrapper when linked inline image.
  65 + '.cke_widget_wrapper>a{' +
  66 + 'display:inline-block' +
  67 + '}' );
  68 + },
  69 +
  70 + init: function( editor ) {
  71 + // Adapts configuration from original image plugin. Should be removed
  72 + // when we'll rename image2 to image.
  73 + var config = editor.config,
  74 + lang = editor.lang.image2,
  75 + image = widgetDef( editor );
  76 +
  77 + // Since filebrowser plugin discovers config properties by dialog (plugin?)
  78 + // names (sic!), this hack will be necessary as long as Image2 is not named
  79 + // Image. And since Image2 will never be Image, for sure some filebrowser logic
  80 + // got to be refined.
  81 + config.filebrowserImage2BrowseUrl = config.filebrowserImageBrowseUrl;
  82 + config.filebrowserImage2UploadUrl = config.filebrowserImageUploadUrl;
  83 +
  84 + // Add custom elementspath names to widget definition.
  85 + image.pathName = lang.pathName;
  86 + image.editables.caption.pathName = lang.pathNameCaption;
  87 +
  88 + // Register the widget.
  89 + editor.widgets.add( 'image', image );
  90 +
  91 + // Add toolbar button for this plugin.
  92 + editor.ui.addButton && editor.ui.addButton( 'Image', {
  93 + label: editor.lang.common.image,
  94 + command: 'image',
  95 + toolbar: 'insert,10'
  96 + } );
  97 +
  98 + // Register context menu option for editing widget.
  99 + if ( editor.contextMenu ) {
  100 + editor.addMenuGroup( 'image', 10 );
  101 +
  102 + editor.addMenuItem( 'image', {
  103 + label: lang.menu,
  104 + command: 'image',
  105 + group: 'image'
  106 + } );
  107 + }
  108 +
  109 + CKEDITOR.dialog.add( 'image2', this.path + 'dialogs/image2.js' );
  110 + },
  111 +
  112 + afterInit: function( editor ) {
  113 + // Integrate with align commands (justify plugin).
  114 + var align = { left: 1, right: 1, center: 1, block: 1 },
  115 + integrate = alignCommandIntegrator( editor );
  116 +
  117 + for ( var value in align )
  118 + integrate( value );
  119 +
  120 + // Integrate with link commands (link plugin).
  121 + linkCommandIntegrator( editor );
  122 + }
  123 + } );
  124 +
  125 + // Wiget states (forms) depending on alignment and configuration.
  126 + //
  127 + // Non-captioned widget (inline styles)
  128 + // ┌──────┬───────────────────────────────┬─────────────────────────────┐
  129 + // │Align │Internal form │Data │
  130 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  131 + // │none │<wrapper> │<img /> │
  132 + // │ │ <img /> │ │
  133 + // │ │</wrapper> │ │
  134 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  135 + // │left │<wrapper style=”float:left”> │<img style=”float:left” /> │
  136 + // │ │ <img /> │ │
  137 + // │ │</wrapper> │ │
  138 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  139 + // │center│<wrapper> │<p style=”text-align:center”>│
  140 + // │ │ <p style=”text-align:center”> │ <img /> │
  141 + // │ │ <img /> │</p> │
  142 + // │ │ </p> │ │
  143 + // │ │</wrapper> │ │
  144 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  145 + // │right │<wrapper style=”float:right”> │<img style=”float:right” /> │
  146 + // │ │ <img /> │ │
  147 + // │ │</wrapper> │ │
  148 + // └──────┴───────────────────────────────┴─────────────────────────────┘
  149 + //
  150 + // Non-captioned widget (config.image2_alignClasses defined)
  151 + // ┌──────┬───────────────────────────────┬─────────────────────────────┐
  152 + // │Align │Internal form │Data │
  153 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  154 + // │none │<wrapper> │<img /> │
  155 + // │ │ <img /> │ │
  156 + // │ │</wrapper> │ │
  157 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  158 + // │left │<wrapper class=”left”> │<img class=”left” /> │
  159 + // │ │ <img /> │ │
  160 + // │ │</wrapper> │ │
  161 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  162 + // │center│<wrapper> │<p class=”center”> │
  163 + // │ │ <p class=”center”> │ <img /> │
  164 + // │ │ <img /> │</p> │
  165 + // │ │ </p> │ │
  166 + // │ │</wrapper> │ │
  167 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  168 + // │right │<wrapper class=”right”> │<img class=”right” /> │
  169 + // │ │ <img /> │ │
  170 + // │ │</wrapper> │ │
  171 + // └──────┴───────────────────────────────┴─────────────────────────────┘
  172 + //
  173 + // Captioned widget (inline styles)
  174 + // ┌──────┬────────────────────────────────────────┬────────────────────────────────────────┐
  175 + // │Align │Internal form │Data │
  176 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  177 + // │none │<wrapper> │<figure /> │
  178 + // │ │ <figure /> │ │
  179 + // │ │</wrapper> │ │
  180 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  181 + // │left │<wrapper style=”float:left”> │<figure style=”float:left” /> │
  182 + // │ │ <figure /> │ │
  183 + // │ │</wrapper> │ │
  184 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  185 + // │center│<wrapper style=”text-align:center”> │<div style=”text-align:center”> │
  186 + // │ │ <figure style=”display:inline-block” />│ <figure style=”display:inline-block” />│
  187 + // │ │</wrapper> │</p> │
  188 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  189 + // │right │<wrapper style=”float:right”> │<figure style=”float:right” /> │
  190 + // │ │ <figure /> │ │
  191 + // │ │</wrapper> │ │
  192 + // └──────┴────────────────────────────────────────┴────────────────────────────────────────┘
  193 + //
  194 + // Captioned widget (config.image2_alignClasses defined)
  195 + // ┌──────┬────────────────────────────────────────┬────────────────────────────────────────┐
  196 + // │Align │Internal form │Data │
  197 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  198 + // │none │<wrapper> │<figure /> │
  199 + // │ │ <figure /> │ │
  200 + // │ │</wrapper> │ │
  201 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  202 + // │left │<wrapper class=”left”> │<figure class=”left” /> │
  203 + // │ │ <figure /> │ │
  204 + // │ │</wrapper> │ │
  205 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  206 + // │center│<wrapper class=”center”> │<div class=”center”> │
  207 + // │ │ <figure /> │ <figure /> │
  208 + // │ │</wrapper> │</p> │
  209 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  210 + // │right │<wrapper class=”right”> │<figure class=”right” /> │
  211 + // │ │ <figure /> │ │
  212 + // │ │</wrapper> │ │
  213 + // └──────┴────────────────────────────────────────┴────────────────────────────────────────┘
  214 + //
  215 + // @param {CKEDITOR.editor}
  216 + // @returns {Object}
  217 + function widgetDef( editor ) {
  218 + var alignClasses = editor.config.image2_alignClasses,
  219 + captionedClass = editor.config.image2_captionedClass;
  220 +
  221 + function deflate() {
  222 + if ( this.deflated )
  223 + return;
  224 +
  225 + // Remember whether widget was focused before destroyed.
  226 + if ( editor.widgets.focused == this.widget )
  227 + this.focused = true;
  228 +
  229 + editor.widgets.destroy( this.widget );
  230 +
  231 + // Mark widget was destroyed.
  232 + this.deflated = true;
  233 + }
  234 +
  235 + function inflate() {
  236 + var editable = editor.editable(),
  237 + doc = editor.document;
  238 +
  239 + // Create a new widget. This widget will be either captioned
  240 + // non-captioned, block or inline according to what is the
  241 + // new state of the widget.
  242 + if ( this.deflated ) {
  243 + this.widget = editor.widgets.initOn( this.element, 'image', this.widget.data );
  244 +
  245 + // Once widget was re-created, it may become an inline element without
  246 + // block wrapper (i.e. when unaligned, end not captioned). Let's do some
  247 + // sort of autoparagraphing here (http://dev.ckeditor.com/ticket/10853).
  248 + if ( this.widget.inline && !( new CKEDITOR.dom.elementPath( this.widget.wrapper, editable ).block ) ) {
  249 + var block = doc.createElement( editor.activeEnterMode == CKEDITOR.ENTER_P ? 'p' : 'div' );
  250 + block.replace( this.widget.wrapper );
  251 + this.widget.wrapper.move( block );
  252 + }
  253 +
  254 + // The focus must be transferred from the old one (destroyed)
  255 + // to the new one (just created).
  256 + if ( this.focused ) {
  257 + this.widget.focus();
  258 + delete this.focused;
  259 + }
  260 +
  261 + delete this.deflated;
  262 + }
  263 +
  264 + // If now widget was destroyed just update wrapper's alignment.
  265 + // According to the new state.
  266 + else {
  267 + setWrapperAlign( this.widget, alignClasses );
  268 + }
  269 + }
  270 +
  271 + return {
  272 + allowedContent: getWidgetAllowedContent( editor ),
  273 +
  274 + requiredContent: 'img[src,alt]',
  275 +
  276 + features: getWidgetFeatures( editor ),
  277 +
  278 + styleableElements: 'img figure',
  279 +
  280 + // This widget converts style-driven dimensions to attributes.
  281 + contentTransformations: [
  282 + [ 'img[width]: sizeToAttribute' ]
  283 + ],
  284 +
  285 + // This widget has an editable caption.
  286 + editables: {
  287 + caption: {
  288 + selector: 'figcaption',
  289 + allowedContent: 'br em strong sub sup u s; a[!href,target]'
  290 + }
  291 + },
  292 +
  293 + parts: {
  294 + image: 'img',
  295 + caption: 'figcaption'
  296 + // parts#link defined in widget#init
  297 + },
  298 +
  299 + // The name of this widget's dialog.
  300 + dialog: 'image2',
  301 +
  302 + // Template of the widget: plain image.
  303 + template: template,
  304 +
  305 + data: function() {
  306 + var features = this.features;
  307 +
  308 + // Image can't be captioned when figcaption is disallowed (http://dev.ckeditor.com/ticket/11004).
  309 + if ( this.data.hasCaption && !editor.filter.checkFeature( features.caption ) )
  310 + this.data.hasCaption = false;
  311 +
  312 + // Image can't be aligned when floating is disallowed (http://dev.ckeditor.com/ticket/11004).
  313 + if ( this.data.align != 'none' && !editor.filter.checkFeature( features.align ) )
  314 + this.data.align = 'none';
  315 +
  316 + // Convert the internal form of the widget from the old state to the new one.
  317 + this.shiftState( {
  318 + widget: this,
  319 + element: this.element,
  320 + oldData: this.oldData,
  321 + newData: this.data,
  322 + deflate: deflate,
  323 + inflate: inflate
  324 + } );
  325 +
  326 + // Update widget.parts.link since it will not auto-update unless widget
  327 + // is destroyed and re-inited.
  328 + if ( !this.data.link ) {
  329 + if ( this.parts.link )
  330 + delete this.parts.link;
  331 + } else {
  332 + if ( !this.parts.link )
  333 + this.parts.link = this.parts.image.getParent();
  334 + }
  335 +
  336 + this.parts.image.setAttributes( {
  337 + src: this.data.src,
  338 +
  339 + // This internal is required by the editor.
  340 + 'data-cke-saved-src': this.data.src,
  341 +
  342 + alt: this.data.alt
  343 + } );
  344 +
  345 + // If shifting non-captioned -> captioned, remove classes
  346 + // related to styles from <img/>.
  347 + if ( this.oldData && !this.oldData.hasCaption && this.data.hasCaption ) {
  348 + for ( var c in this.data.classes )
  349 + this.parts.image.removeClass( c );
  350 + }
  351 +
  352 + // Set dimensions of the image according to gathered data.
  353 + // Do it only when the attributes are allowed (http://dev.ckeditor.com/ticket/11004).
  354 + if ( editor.filter.checkFeature( features.dimension ) )
  355 + setDimensions( this );
  356 +
  357 + // Cache current data.
  358 + this.oldData = CKEDITOR.tools.extend( {}, this.data );
  359 + },
  360 +
  361 + init: function() {
  362 + var helpers = CKEDITOR.plugins.image2,
  363 + image = this.parts.image,
  364 + data = {
  365 + hasCaption: !!this.parts.caption,
  366 + src: image.getAttribute( 'src' ),
  367 + alt: image.getAttribute( 'alt' ) || '',
  368 + width: image.getAttribute( 'width' ) || '',
  369 + height: image.getAttribute( 'height' ) || '',
  370 +
  371 + // Lock ratio is on by default (http://dev.ckeditor.com/ticket/10833).
  372 + lock: this.ready ? helpers.checkHasNaturalRatio( image ) : true
  373 + };
  374 +
  375 + // If we used 'a' in widget#parts definition, it could happen that
  376 + // selected element is a child of widget.parts#caption. Since there's no clever
  377 + // way to solve it with CSS selectors, it's done like that. (http://dev.ckeditor.com/ticket/11783).
  378 + var link = image.getAscendant( 'a' );
  379 +
  380 + if ( link && this.wrapper.contains( link ) )
  381 + this.parts.link = link;
  382 +
  383 + // Depending on configuration, read style/class from element and
  384 + // then remove it. Removed style/class will be set on wrapper in #data listener.
  385 + // Note: Center alignment is detected during upcast, so only left/right cases
  386 + // are checked below.
  387 + if ( !data.align ) {
  388 + var alignElement = data.hasCaption ? this.element : image;
  389 +
  390 + // Read the initial left/right alignment from the class set on element.
  391 + if ( alignClasses ) {
  392 + if ( alignElement.hasClass( alignClasses[ 0 ] ) ) {
  393 + data.align = 'left';
  394 + } else if ( alignElement.hasClass( alignClasses[ 2 ] ) ) {
  395 + data.align = 'right';
  396 + }
  397 +
  398 + if ( data.align ) {
  399 + alignElement.removeClass( alignClasses[ alignmentsObj[ data.align ] ] );
  400 + } else {
  401 + data.align = 'none';
  402 + }
  403 + }
  404 + // Read initial float style from figure/image and then remove it.
  405 + else {
  406 + data.align = alignElement.getStyle( 'float' ) || 'none';
  407 + alignElement.removeStyle( 'float' );
  408 + }
  409 + }
  410 +
  411 + // Update data.link object with attributes if the link has been discovered.
  412 + if ( editor.plugins.link && this.parts.link ) {
  413 + data.link = helpers.getLinkAttributesParser()( editor, this.parts.link );
  414 +
  415 + // Get rid of cke_widget_* classes in data. Otherwise
  416 + // they might appear in link dialog.
  417 + var advanced = data.link.advanced;
  418 + if ( advanced && advanced.advCSSClasses ) {
  419 + advanced.advCSSClasses = CKEDITOR.tools.trim( advanced.advCSSClasses.replace( /cke_\S+/, '' ) );
  420 + }
  421 + }
  422 +
  423 + // Get rid of extra vertical space when there's no caption.
  424 + // It will improve the look of the resizer.
  425 + this.wrapper[ ( data.hasCaption ? 'remove' : 'add' ) + 'Class' ]( 'cke_image_nocaption' );
  426 +
  427 + this.setData( data );
  428 +
  429 + // Setup dynamic image resizing with mouse.
  430 + // Don't initialize resizer when dimensions are disallowed (http://dev.ckeditor.com/ticket/11004).
  431 + // Don't initialize resizer when editor.readOnly is set to true (#719).
  432 + if ( editor.filter.checkFeature( this.features.dimension ) && editor.config.image2_disableResizer !== true && editor.readOnly != true ) {
  433 + setupResizer( this );
  434 + }
  435 +
  436 + this.shiftState = helpers.stateShifter( this.editor );
  437 +
  438 + // Add widget editing option to its context menu.
  439 + this.on( 'contextMenu', function( evt ) {
  440 + evt.data.image = CKEDITOR.TRISTATE_OFF;
  441 +
  442 + // Integrate context menu items for link.
  443 + // Note that widget may be wrapped in a link, which
  444 + // does not belong to that widget (http://dev.ckeditor.com/ticket/11814).
  445 + if ( this.parts.link || this.wrapper.getAscendant( 'a' ) )
  446 + evt.data.link = evt.data.unlink = CKEDITOR.TRISTATE_OFF;
  447 + } );
  448 +
  449 + // Pass the reference to this widget to the dialog.
  450 + this.on( 'dialog', function( evt ) {
  451 + evt.data.widget = this;
  452 + }, this );
  453 + },
  454 +
  455 + // Overrides default method to handle internal mutability of Image2.
  456 + // @see CKEDITOR.plugins.widget#addClass
  457 + addClass: function( className ) {
  458 + getStyleableElement( this ).addClass( className );
  459 + },
  460 +
  461 + // Overrides default method to handle internal mutability of Image2.
  462 + // @see CKEDITOR.plugins.widget#hasClass
  463 + hasClass: function( className ) {
  464 + return getStyleableElement( this ).hasClass( className );
  465 + },
  466 +
  467 + // Overrides default method to handle internal mutability of Image2.
  468 + // @see CKEDITOR.plugins.widget#removeClass
  469 + removeClass: function( className ) {
  470 + getStyleableElement( this ).removeClass( className );
  471 + },
  472 +
  473 + // Overrides default method to handle internal mutability of Image2.
  474 + // @see CKEDITOR.plugins.widget#getClasses
  475 + getClasses: ( function() {
  476 + var classRegex = new RegExp( '^(' + [].concat( captionedClass, alignClasses ).join( '|' ) + ')$' );
  477 +
  478 + return function() {
  479 + var classes = this.repository.parseElementClasses( getStyleableElement( this ).getAttribute( 'class' ) );
  480 +
  481 + // Neither config.image2_captionedClass nor config.image2_alignClasses
  482 + // do not belong to style classes.
  483 + for ( var c in classes ) {
  484 + if ( classRegex.test( c ) )
  485 + delete classes[ c ];
  486 + }
  487 +
  488 + return classes;
  489 + };
  490 + } )(),
  491 +
  492 + upcast: upcastWidgetElement( editor ),
  493 + downcast: downcastWidgetElement( editor ),
  494 +
  495 + getLabel: function() {
  496 + var label = ( this.data.alt || '' ) + ' ' + this.pathName;
  497 +
  498 + return this.editor.lang.widget.label.replace( /%1/, label );
  499 + }
  500 + };
  501 + }
  502 +
  503 + /**
  504 + * A set of Enhanced Image (image2) plugin helpers.
  505 + *
  506 + * @class
  507 + * @singleton
  508 + */
  509 + CKEDITOR.plugins.image2 = {
  510 + stateShifter: function( editor ) {
  511 + // Tag name used for centering non-captioned widgets.
  512 + var doc = editor.document,
  513 + alignClasses = editor.config.image2_alignClasses,
  514 + captionedClass = editor.config.image2_captionedClass,
  515 + editable = editor.editable(),
  516 +
  517 + // The order that stateActions get executed. It matters!
  518 + shiftables = [ 'hasCaption', 'align', 'link' ];
  519 +
  520 + // Atomic procedures, one per state variable.
  521 + var stateActions = {
  522 + align: function( shift, oldValue, newValue ) {
  523 + var el = shift.element;
  524 +
  525 + // Alignment changed.
  526 + if ( shift.changed.align ) {
  527 + // No caption in the new state.
  528 + if ( !shift.newData.hasCaption ) {
  529 + // Changed to "center" (non-captioned).
  530 + if ( newValue == 'center' ) {
  531 + shift.deflate();
  532 + shift.element = wrapInCentering( editor, el );
  533 + }
  534 +
  535 + // Changed to "non-center" from "center" while caption removed.
  536 + if ( !shift.changed.hasCaption && oldValue == 'center' && newValue != 'center' ) {
  537 + shift.deflate();
  538 + shift.element = unwrapFromCentering( el );
  539 + }
  540 + }
  541 + }
  542 +
  543 + // Alignment remains and "center" removed caption.
  544 + else if ( newValue == 'center' && shift.changed.hasCaption && !shift.newData.hasCaption ) {
  545 + shift.deflate();
  546 + shift.element = wrapInCentering( editor, el );
  547 + }
  548 +
  549 + // Finally set display for figure.
  550 + if ( !alignClasses && el.is( 'figure' ) ) {
  551 + if ( newValue == 'center' )
  552 + el.setStyle( 'display', 'inline-block' );
  553 + else
  554 + el.removeStyle( 'display' );
  555 + }
  556 + },
  557 +
  558 + hasCaption: function( shift, oldValue, newValue ) {
  559 + // This action is for real state change only.
  560 + if ( !shift.changed.hasCaption )
  561 + return;
  562 +
  563 + // Get <img/> or <a><img/></a> from widget. Note that widget element might itself
  564 + // be what we're looking for. Also element can be <p style="text-align:center"><a>...</a></p>.
  565 + var imageOrLink;
  566 + if ( shift.element.is( { img: 1, a: 1 } ) )
  567 + imageOrLink = shift.element;
  568 + else
  569 + imageOrLink = shift.element.findOne( 'a,img' );
  570 +
  571 + // Switching hasCaption always destroys the widget.
  572 + shift.deflate();
  573 +
  574 + // There was no caption, but the caption is to be added.
  575 + if ( newValue ) {
  576 + // Create new <figure> from widget template.
  577 + var figure = CKEDITOR.dom.element.createFromHtml( templateBlock.output( {
  578 + captionedClass: captionedClass,
  579 + captionPlaceholder: editor.lang.image2.captionPlaceholder
  580 + } ), doc );
  581 +
  582 + // Replace element with <figure>.
  583 + replaceSafely( figure, shift.element );
  584 +
  585 + // Use old <img/> or <a><img/></a> instead of the one from the template,
  586 + // so we won't lose additional attributes.
  587 + imageOrLink.replace( figure.findOne( 'img' ) );
  588 +
  589 + // Update widget's element.
  590 + shift.element = figure;
  591 + }
  592 +
  593 + // The caption was present, but now it's to be removed.
  594 + else {
  595 + // Unwrap <img/> or <a><img/></a> from figure.
  596 + imageOrLink.replace( shift.element );
  597 +
  598 + // Update widget's element.
  599 + shift.element = imageOrLink;
  600 + }
  601 + },
  602 +
  603 + link: function( shift, oldValue, newValue ) {
  604 + if ( shift.changed.link ) {
  605 + var img = shift.element.is( 'img' ) ?
  606 + shift.element : shift.element.findOne( 'img' ),
  607 + link = shift.element.is( 'a' ) ?
  608 + shift.element : shift.element.findOne( 'a' ),
  609 + // Why deflate:
  610 + // If element is <img/>, it will be wrapped into <a>,
  611 + // which becomes a new widget.element.
  612 + // If element is <a><img/></a>, it will be unlinked
  613 + // so <img/> becomes a new widget.element.
  614 + needsDeflate = ( shift.element.is( 'a' ) && !newValue ) || ( shift.element.is( 'img' ) && newValue ),
  615 + newEl;
  616 +
  617 + if ( needsDeflate )
  618 + shift.deflate();
  619 +
  620 + // If unlinked the image, returned element is <img>.
  621 + if ( !newValue )
  622 + newEl = unwrapFromLink( link );
  623 + else {
  624 + // If linked the image, returned element is <a>.
  625 + if ( !oldValue )
  626 + newEl = wrapInLink( img, shift.newData.link );
  627 +
  628 + // Set and remove all attributes associated with this state.
  629 + var attributes = CKEDITOR.plugins.image2.getLinkAttributesGetter()( editor, newValue );
  630 +
  631 + if ( !CKEDITOR.tools.isEmpty( attributes.set ) )
  632 + ( newEl || link ).setAttributes( attributes.set );
  633 +
  634 + if ( attributes.removed.length )
  635 + ( newEl || link ).removeAttributes( attributes.removed );
  636 + }
  637 +
  638 + if ( needsDeflate )
  639 + shift.element = newEl;
  640 + }
  641 + }
  642 + };
  643 +
  644 + function wrapInCentering( editor, element ) {
  645 + var attribsAndStyles = {};
  646 +
  647 + if ( alignClasses )
  648 + attribsAndStyles.attributes = { 'class': alignClasses[ 1 ] };
  649 + else
  650 + attribsAndStyles.styles = { 'text-align': 'center' };
  651 +
  652 + // There's no gentle way to center inline element with CSS, so create p/div
  653 + // that wraps widget contents and does the trick either with style or class.
  654 + var center = doc.createElement(
  655 + editor.activeEnterMode == CKEDITOR.ENTER_P ? 'p' : 'div', attribsAndStyles );
  656 +
  657 + // Replace element with centering wrapper.
  658 + replaceSafely( center, element );
  659 + element.move( center );
  660 +
  661 + return center;
  662 + }
  663 +
  664 + function unwrapFromCentering( element ) {
  665 + var imageOrLink = element.findOne( 'a,img' );
  666 +
  667 + imageOrLink.replace( element );
  668 +
  669 + return imageOrLink;
  670 + }
  671 +
  672 + // Wraps <img/> -> <a><img/></a>.
  673 + // Returns reference to <a>.
  674 + //
  675 + // @param {CKEDITOR.dom.element} img
  676 + // @param {Object} linkData
  677 + // @returns {CKEDITOR.dom.element}
  678 + function wrapInLink( img, linkData ) {
  679 + var link = doc.createElement( 'a', {
  680 + attributes: {
  681 + href: linkData.url
  682 + }
  683 + } );
  684 +
  685 + link.replace( img );
  686 + img.move( link );
  687 +
  688 + return link;
  689 + }
  690 +
  691 + // De-wraps <a><img/></a> -> <img/>.
  692 + // Returns the reference to <img/>
  693 + //
  694 + // @param {CKEDITOR.dom.element} link
  695 + // @returns {CKEDITOR.dom.element}
  696 + function unwrapFromLink( link ) {
  697 + var img = link.findOne( 'img' );
  698 +
  699 + img.replace( link );
  700 +
  701 + return img;
  702 + }
  703 +
  704 + function replaceSafely( replacing, replaced ) {
  705 + if ( replaced.getParent() ) {
  706 + var range = editor.createRange();
  707 +
  708 + range.moveToPosition( replaced, CKEDITOR.POSITION_BEFORE_START );
  709 +
  710 + // Remove old element. Do it before insertion to avoid a case when
  711 + // element is moved from 'replaced' element before it, what creates
  712 + // a tricky case which insertElementIntorRange does not handle.
  713 + replaced.remove();
  714 +
  715 + editable.insertElementIntoRange( replacing, range );
  716 + }
  717 + else {
  718 + replacing.replace( replaced );
  719 + }
  720 + }
  721 +
  722 + return function( shift ) {
  723 + var name, i;
  724 +
  725 + shift.changed = {};
  726 +
  727 + for ( i = 0; i < shiftables.length; i++ ) {
  728 + name = shiftables[ i ];
  729 +
  730 + shift.changed[ name ] = shift.oldData ?
  731 + shift.oldData[ name ] !== shift.newData[ name ] : false;
  732 + }
  733 +
  734 + // Iterate over possible state variables.
  735 + for ( i = 0; i < shiftables.length; i++ ) {
  736 + name = shiftables[ i ];
  737 +
  738 + stateActions[ name ]( shift,
  739 + shift.oldData ? shift.oldData[ name ] : null,
  740 + shift.newData[ name ] );
  741 + }
  742 +
  743 + shift.inflate();
  744 + };
  745 + },
  746 +
  747 + /**
  748 + * Checks whether the current image ratio matches the natural one
  749 + * by comparing dimensions.
  750 + *
  751 + * @param {CKEDITOR.dom.element} image
  752 + * @returns {Boolean}
  753 + */
  754 + checkHasNaturalRatio: function( image ) {
  755 + var $ = image.$,
  756 + natural = this.getNatural( image );
  757 +
  758 + // The reason for two alternative comparisons is that the rounding can come from
  759 + // both dimensions, e.g. there are two cases:
  760 + // 1. height is computed as a rounded relation of the real height and the value of width,
  761 + // 2. width is computed as a rounded relation of the real width and the value of heigh.
  762 + return Math.round( $.clientWidth / natural.width * natural.height ) == $.clientHeight ||
  763 + Math.round( $.clientHeight / natural.height * natural.width ) == $.clientWidth;
  764 + },
  765 +
  766 + /**
  767 + * Returns natural dimensions of the image. For modern browsers
  768 + * it uses natural(Width|Height). For old ones (IE8) it creates
  769 + * a new image and reads the dimensions.
  770 + *
  771 + * @param {CKEDITOR.dom.element} image
  772 + * @returns {Object}
  773 + */
  774 + getNatural: function( image ) {
  775 + var dimensions;
  776 +
  777 + if ( image.$.naturalWidth ) {
  778 + dimensions = {
  779 + width: image.$.naturalWidth,
  780 + height: image.$.naturalHeight
  781 + };
  782 + } else {
  783 + var img = new Image();
  784 + img.src = image.getAttribute( 'src' );
  785 +
  786 + dimensions = {
  787 + width: img.width,
  788 + height: img.height
  789 + };
  790 + }
  791 +
  792 + return dimensions;
  793 + },
  794 +
  795 + /**
  796 + * Returns an attribute getter function. Default getter comes from the Link plugin
  797 + * and is documented by {@link CKEDITOR.plugins.link#getLinkAttributes}.
  798 + *
  799 + * **Note:** It is possible to override this method and use a custom getter e.g.
  800 + * in the absence of the Link plugin.
  801 + *
  802 + * **Note:** If a custom getter is used, a data model format it produces
  803 + * must be compatible with {@link CKEDITOR.plugins.link#getLinkAttributes}.
  804 + *
  805 + * **Note:** A custom getter must understand the data model format produced by
  806 + * {@link #getLinkAttributesParser} to work correctly.
  807 + *
  808 + * @returns {Function} A function that gets (composes) link attributes.
  809 + * @since 4.5.5
  810 + */
  811 + getLinkAttributesGetter: function() {
  812 + // http://dev.ckeditor.com/ticket/13885
  813 + return CKEDITOR.plugins.link.getLinkAttributes;
  814 + },
  815 +
  816 + /**
  817 + * Returns an attribute parser function. Default parser comes from the Link plugin
  818 + * and is documented by {@link CKEDITOR.plugins.link#parseLinkAttributes}.
  819 + *
  820 + * **Note:** It is possible to override this method and use a custom parser e.g.
  821 + * in the absence of the Link plugin.
  822 + *
  823 + * **Note:** If a custom parser is used, a data model format produced by the parser
  824 + * must be compatible with {@link #getLinkAttributesGetter}.
  825 + *
  826 + * **Note:** If a custom parser is used, it should be compatible with the
  827 + * {@link CKEDITOR.plugins.link#parseLinkAttributes} data model format. Otherwise the
  828 + * Link plugin dialog may not be populated correctly with parsed data. However
  829 + * as long as Enhanced Image is **not** used with the Link plugin dialog, any custom data model
  830 + * will work, being stored as an internal property of Enhanced Image widget's data only.
  831 + *
  832 + * @returns {Function} A function that parses attributes.
  833 + * @since 4.5.5
  834 + */
  835 + getLinkAttributesParser: function() {
  836 + // http://dev.ckeditor.com/ticket/13885
  837 + return CKEDITOR.plugins.link.parseLinkAttributes;
  838 + }
  839 + };
  840 +
  841 + function setWrapperAlign( widget, alignClasses ) {
  842 + var wrapper = widget.wrapper,
  843 + align = widget.data.align,
  844 + hasCaption = widget.data.hasCaption;
  845 +
  846 + if ( alignClasses ) {
  847 + // Remove all align classes first.
  848 + for ( var i = 3; i--; )
  849 + wrapper.removeClass( alignClasses[ i ] );
  850 +
  851 + if ( align == 'center' ) {
  852 + // Avoid touching non-captioned, centered widgets because
  853 + // they have the class set on the element instead of wrapper:
  854 + //
  855 + // <div class="cke_widget_wrapper">
  856 + // <p class="center-class">
  857 + // <img />
  858 + // </p>
  859 + // </div>
  860 + if ( hasCaption ) {
  861 + wrapper.addClass( alignClasses[ 1 ] );
  862 + }
  863 + } else if ( align != 'none' ) {
  864 + wrapper.addClass( alignClasses[ alignmentsObj[ align ] ] );
  865 + }
  866 + } else {
  867 + if ( align == 'center' ) {
  868 + if ( hasCaption )
  869 + wrapper.setStyle( 'text-align', 'center' );
  870 + else
  871 + wrapper.removeStyle( 'text-align' );
  872 +
  873 + wrapper.removeStyle( 'float' );
  874 + }
  875 + else {
  876 + if ( align == 'none' )
  877 + wrapper.removeStyle( 'float' );
  878 + else
  879 + wrapper.setStyle( 'float', align );
  880 +
  881 + wrapper.removeStyle( 'text-align' );
  882 + }
  883 + }
  884 + }
  885 +
  886 + // Returns a function that creates widgets from all <img> and
  887 + // <figure class="{config.image2_captionedClass}"> elements.
  888 + //
  889 + // @param {CKEDITOR.editor} editor
  890 + // @returns {Function}
  891 + function upcastWidgetElement( editor ) {
  892 + var isCenterWrapper = centerWrapperChecker( editor ),
  893 + captionedClass = editor.config.image2_captionedClass;
  894 +
  895 + // @param {CKEDITOR.htmlParser.element} el
  896 + // @param {Object} data
  897 + return function( el, data ) {
  898 + var dimensions = { width: 1, height: 1 },
  899 + name = el.name,
  900 + image;
  901 +
  902 + // http://dev.ckeditor.com/ticket/11110 Don't initialize on pasted fake objects.
  903 + if ( el.attributes[ 'data-cke-realelement' ] )
  904 + return;
  905 +
  906 + // If a center wrapper is found, there are 3 possible cases:
  907 + //
  908 + // 1. <div style="text-align:center"><figure>...</figure></div>.
  909 + // In this case centering is done with a class set on widget.wrapper.
  910 + // Simply replace centering wrapper with figure (it's no longer necessary).
  911 + //
  912 + // 2. <p style="text-align:center"><img/></p>.
  913 + // Nothing to do here: <p> remains for styling purposes.
  914 + //
  915 + // 3. <div style="text-align:center"><img/></div>.
  916 + // Nothing to do here (2.) but that case is only possible in enterMode different
  917 + // than ENTER_P.
  918 + if ( isCenterWrapper( el ) ) {
  919 + if ( name == 'div' ) {
  920 + var figure = el.getFirst( 'figure' );
  921 +
  922 + // Case #1.
  923 + if ( figure ) {
  924 + el.replaceWith( figure );
  925 + el = figure;
  926 + }
  927 + }
  928 + // Cases #2 and #3 (handled transparently)
  929 +
  930 + // If there's a centering wrapper, save it in data.
  931 + data.align = 'center';
  932 +
  933 + // Image can be wrapped in link <a><img/></a>.
  934 + image = el.getFirst( 'img' ) || el.getFirst( 'a' ).getFirst( 'img' );
  935 + }
  936 +
  937 + // No center wrapper has been found.
  938 + else if ( name == 'figure' && el.hasClass( captionedClass ) ) {
  939 + image = el.getFirst( 'img' ) || el.getFirst( 'a' ).getFirst( 'img' );
  940 +
  941 + // Upcast linked image like <a><img/></a>.
  942 + } else if ( isLinkedOrStandaloneImage( el ) ) {
  943 + image = el.name == 'a' ? el.children[ 0 ] : el;
  944 + }
  945 +
  946 + if ( !image )
  947 + return;
  948 +
  949 + // If there's an image, then cool, we got a widget.
  950 + // Now just remove dimension attributes expressed with %.
  951 + for ( var d in dimensions ) {
  952 + var dimension = image.attributes[ d ];
  953 +
  954 + if ( dimension && dimension.match( regexPercent ) )
  955 + delete image.attributes[ d ];
  956 + }
  957 +
  958 + return el;
  959 + };
  960 + }
  961 +
  962 + // Returns a function which transforms the widget to the external format
  963 + // according to the current configuration.
  964 + //
  965 + // @param {CKEDITOR.editor}
  966 + function downcastWidgetElement( editor ) {
  967 + var alignClasses = editor.config.image2_alignClasses;
  968 +
  969 + // @param {CKEDITOR.htmlParser.element} el
  970 + return function( el ) {
  971 + // In case of <a><img/></a>, <img/> is the element to hold
  972 + // inline styles or classes (image2_alignClasses).
  973 + var attrsHolder = el.name == 'a' ? el.getFirst() : el,
  974 + attrs = attrsHolder.attributes,
  975 + align = this.data.align;
  976 +
  977 + // De-wrap the image from resize handle wrapper.
  978 + // Only block widgets have one.
  979 + if ( !this.inline ) {
  980 + var resizeWrapper = el.getFirst( 'span' );
  981 +
  982 + if ( resizeWrapper )
  983 + resizeWrapper.replaceWith( resizeWrapper.getFirst( { img: 1, a: 1 } ) );
  984 + }
  985 +
  986 + if ( align && align != 'none' ) {
  987 + var styles = CKEDITOR.tools.parseCssText( attrs.style || '' );
  988 +
  989 + // When the widget is captioned (<figure>) and internally centering is done
  990 + // with widget's wrapper style/class, in the external data representation,
  991 + // <figure> must be wrapped with an element holding an style/class:
  992 + //
  993 + // <div style="text-align:center">
  994 + // <figure class="image" style="display:inline-block">...</figure>
  995 + // </div>
  996 + // or
  997 + // <div class="some-center-class">
  998 + // <figure class="image">...</figure>
  999 + // </div>
  1000 + //
  1001 + if ( align == 'center' && el.name == 'figure' ) {
  1002 + el = el.wrapWith( new CKEDITOR.htmlParser.element( 'div',
  1003 + alignClasses ? { 'class': alignClasses[ 1 ] } : { style: 'text-align:center' } ) );
  1004 + }
  1005 +
  1006 + // If left/right, add float style to the downcasted element.
  1007 + else if ( align in { left: 1, right: 1 } ) {
  1008 + if ( alignClasses )
  1009 + attrsHolder.addClass( alignClasses[ alignmentsObj[ align ] ] );
  1010 + else
  1011 + styles[ 'float' ] = align;
  1012 + }
  1013 +
  1014 + // Update element styles.
  1015 + if ( !alignClasses && !CKEDITOR.tools.isEmpty( styles ) )
  1016 + attrs.style = CKEDITOR.tools.writeCssText( styles );
  1017 + }
  1018 +
  1019 + return el;
  1020 + };
  1021 + }
  1022 +
  1023 + // Returns a function that checks if an element is a centering wrapper.
  1024 + //
  1025 + // @param {CKEDITOR.editor} editor
  1026 + // @returns {Function}
  1027 + function centerWrapperChecker( editor ) {
  1028 + var captionedClass = editor.config.image2_captionedClass,
  1029 + alignClasses = editor.config.image2_alignClasses,
  1030 + validChildren = { figure: 1, a: 1, img: 1 };
  1031 +
  1032 + return function( el ) {
  1033 + // Wrapper must be either <div> or <p>.
  1034 + if ( !( el.name in { div: 1, p: 1 } ) )
  1035 + return false;
  1036 +
  1037 + var children = el.children;
  1038 +
  1039 + // Centering wrapper can have only one child.
  1040 + if ( children.length !== 1 )
  1041 + return false;
  1042 +
  1043 + var child = children[ 0 ];
  1044 +
  1045 + // Only <figure> or <img /> can be first (only) child of centering wrapper,
  1046 + // regardless of its type.
  1047 + if ( !( child.name in validChildren ) )
  1048 + return false;
  1049 +
  1050 + // If centering wrapper is <p>, only <img /> can be the child.
  1051 + // <p style="text-align:center"><img /></p>
  1052 + if ( el.name == 'p' ) {
  1053 + if ( !isLinkedOrStandaloneImage( child ) )
  1054 + return false;
  1055 + }
  1056 + // Centering <div> can hold <img/> or <figure>, depending on enterMode.
  1057 + else {
  1058 + // If a <figure> is the first (only) child, it must have a class.
  1059 + // <div style="text-align:center"><figure>...</figure><div>
  1060 + if ( child.name == 'figure' ) {
  1061 + if ( !child.hasClass( captionedClass ) )
  1062 + return false;
  1063 + } else {
  1064 + // Centering <div> can hold <img/> or <a><img/></a> only when enterMode
  1065 + // is ENTER_(BR|DIV).
  1066 + // <div style="text-align:center"><img /></div>
  1067 + // <div style="text-align:center"><a><img /></a></div>
  1068 + if ( editor.enterMode == CKEDITOR.ENTER_P )
  1069 + return false;
  1070 +
  1071 + // Regardless of enterMode, a child which is not <figure> must be
  1072 + // either <img/> or <a><img/></a>.
  1073 + if ( !isLinkedOrStandaloneImage( child ) )
  1074 + return false;
  1075 + }
  1076 + }
  1077 +
  1078 + // Centering wrapper got to be... centering. If image2_alignClasses are defined,
  1079 + // check for centering class. Otherwise, check the style.
  1080 + if ( alignClasses ? el.hasClass( alignClasses[ 1 ] ) :
  1081 + CKEDITOR.tools.parseCssText( el.attributes.style || '', true )[ 'text-align' ] == 'center' )
  1082 + return true;
  1083 +
  1084 + return false;
  1085 + };
  1086 + }
  1087 +
  1088 + // Checks whether element is <img/> or <a><img/></a>.
  1089 + //
  1090 + // @param {CKEDITOR.htmlParser.element}
  1091 + function isLinkedOrStandaloneImage( el ) {
  1092 + if ( el.name == 'img' )
  1093 + return true;
  1094 + else if ( el.name == 'a' )
  1095 + return el.children.length == 1 && el.getFirst( 'img' );
  1096 +
  1097 + return false;
  1098 + }
  1099 +
  1100 + // Sets width and height of the widget image according to current widget data.
  1101 + //
  1102 + // @param {CKEDITOR.plugins.widget} widget
  1103 + function setDimensions( widget ) {
  1104 + var data = widget.data,
  1105 + dimensions = { width: data.width, height: data.height },
  1106 + image = widget.parts.image;
  1107 +
  1108 + for ( var d in dimensions ) {
  1109 + if ( dimensions[ d ] )
  1110 + image.setAttribute( d, dimensions[ d ] );
  1111 + else
  1112 + image.removeAttribute( d );
  1113 + }
  1114 + }
  1115 +
  1116 + // Defines all features related to drag-driven image resizing.
  1117 + //
  1118 + // @param {CKEDITOR.plugins.widget} widget
  1119 + function setupResizer( widget ) {
  1120 + var editor = widget.editor,
  1121 + editable = editor.editable(),
  1122 + doc = editor.document,
  1123 +
  1124 + // Store the resizer in a widget for testing (http://dev.ckeditor.com/ticket/11004).
  1125 + resizer = widget.resizer = doc.createElement( 'span' );
  1126 +
  1127 + resizer.addClass( 'cke_image_resizer' );
  1128 + resizer.setAttribute( 'title', editor.lang.image2.resizer );
  1129 + resizer.append( new CKEDITOR.dom.text( '\u200b', doc ) );
  1130 +
  1131 + // Inline widgets don't need a resizer wrapper as an image spans the entire widget.
  1132 + if ( !widget.inline ) {
  1133 + var imageOrLink = widget.parts.link || widget.parts.image,
  1134 + oldResizeWrapper = imageOrLink.getParent(),
  1135 + resizeWrapper = doc.createElement( 'span' );
  1136 +
  1137 + resizeWrapper.addClass( 'cke_image_resizer_wrapper' );
  1138 + resizeWrapper.append( imageOrLink );
  1139 + resizeWrapper.append( resizer );
  1140 + widget.element.append( resizeWrapper, true );
  1141 +
  1142 + // Remove the old wrapper which could came from e.g. pasted HTML
  1143 + // and which could be corrupted (e.g. resizer span has been lost).
  1144 + if ( oldResizeWrapper.is( 'span' ) )
  1145 + oldResizeWrapper.remove();
  1146 + } else {
  1147 + widget.wrapper.append( resizer );
  1148 + }
  1149 +
  1150 + // Calculate values of size variables and mouse offsets.
  1151 + resizer.on( 'mousedown', function( evt ) {
  1152 + var image = widget.parts.image,
  1153 +
  1154 + // "factor" can be either 1 or -1. I.e.: For right-aligned images, we need to
  1155 + // subtract the difference to get proper width, etc. Without "factor",
  1156 + // resizer starts working the opposite way.
  1157 + factor = widget.data.align == 'right' ? -1 : 1,
  1158 +
  1159 + // The x-coordinate of the mouse relative to the screen
  1160 + // when button gets pressed.
  1161 + startX = evt.data.$.screenX,
  1162 + startY = evt.data.$.screenY,
  1163 +
  1164 + // The initial dimensions and aspect ratio of the image.
  1165 + startWidth = image.$.clientWidth,
  1166 + startHeight = image.$.clientHeight,
  1167 + ratio = startWidth / startHeight,
  1168 +
  1169 + listeners = [],
  1170 +
  1171 + // A class applied to editable during resizing.
  1172 + cursorClass = 'cke_image_s' + ( !~factor ? 'w' : 'e' ),
  1173 +
  1174 + nativeEvt, newWidth, newHeight, updateData,
  1175 + moveDiffX, moveDiffY, moveRatio;
  1176 +
  1177 + // Save the undo snapshot first: before resizing.
  1178 + editor.fire( 'saveSnapshot' );
  1179 +
  1180 + // Mousemove listeners are removed on mouseup.
  1181 + attachToDocuments( 'mousemove', onMouseMove, listeners );
  1182 +
  1183 + // Clean up the mousemove listener. Update widget data if valid.
  1184 + attachToDocuments( 'mouseup', onMouseUp, listeners );
  1185 +
  1186 + // The entire editable will have the special cursor while resizing goes on.
  1187 + editable.addClass( cursorClass );
  1188 +
  1189 + // This is to always keep the resizer element visible while resizing.
  1190 + resizer.addClass( 'cke_image_resizing' );
  1191 +
  1192 + // Attaches an event to a global document if inline editor.
  1193 + // Additionally, if classic (`iframe`-based) editor, also attaches the same event to `iframe`'s document.
  1194 + function attachToDocuments( name, callback, collection ) {
  1195 + var globalDoc = CKEDITOR.document,
  1196 + listeners = [];
  1197 +
  1198 + if ( !doc.equals( globalDoc ) )
  1199 + listeners.push( globalDoc.on( name, callback ) );
  1200 +
  1201 + listeners.push( doc.on( name, callback ) );
  1202 +
  1203 + if ( collection ) {
  1204 + for ( var i = listeners.length; i--; )
  1205 + collection.push( listeners.pop() );
  1206 + }
  1207 + }
  1208 +
  1209 + // Calculate with first, and then adjust height, preserving ratio.
  1210 + function adjustToX() {
  1211 + newWidth = startWidth + factor * moveDiffX;
  1212 + newHeight = Math.round( newWidth / ratio );
  1213 + }
  1214 +
  1215 + // Calculate height first, and then adjust width, preserving ratio.
  1216 + function adjustToY() {
  1217 + newHeight = startHeight - moveDiffY;
  1218 + newWidth = Math.round( newHeight * ratio );
  1219 + }
  1220 +
  1221 + // This is how variables refer to the geometry.
  1222 + // Note: x corresponds to moveOffset, this is the position of mouse
  1223 + // Note: o corresponds to [startX, startY].
  1224 + //
  1225 + // +--------------+--------------+
  1226 + // | | |
  1227 + // | I | II |
  1228 + // | | |
  1229 + // +------------- o -------------+ _ _ _
  1230 + // | | | ^
  1231 + // | VI | III | | moveDiffY
  1232 + // | | x _ _ _ _ _ v
  1233 + // +--------------+---------|----+
  1234 + // | |
  1235 + // <------->
  1236 + // moveDiffX
  1237 + function onMouseMove( evt ) {
  1238 + nativeEvt = evt.data.$;
  1239 +
  1240 + // This is how far the mouse is from the point the button was pressed.
  1241 + moveDiffX = nativeEvt.screenX - startX;
  1242 + moveDiffY = startY - nativeEvt.screenY;
  1243 +
  1244 + // This is the aspect ratio of the move difference.
  1245 + moveRatio = Math.abs( moveDiffX / moveDiffY );
  1246 +
  1247 + // Left, center or none-aligned widget.
  1248 + if ( factor == 1 ) {
  1249 + if ( moveDiffX <= 0 ) {
  1250 + // Case: IV.
  1251 + if ( moveDiffY <= 0 )
  1252 + adjustToX();
  1253 +
  1254 + // Case: I.
  1255 + else {
  1256 + if ( moveRatio >= ratio )
  1257 + adjustToX();
  1258 + else
  1259 + adjustToY();
  1260 + }
  1261 + } else {
  1262 + // Case: III.
  1263 + if ( moveDiffY <= 0 ) {
  1264 + if ( moveRatio >= ratio )
  1265 + adjustToY();
  1266 + else
  1267 + adjustToX();
  1268 + }
  1269 +
  1270 + // Case: II.
  1271 + else {
  1272 + adjustToY();
  1273 + }
  1274 + }
  1275 + }
  1276 +
  1277 + // Right-aligned widget. It mirrors behaviours, so I becomes II,
  1278 + // IV becomes III and vice-versa.
  1279 + else {
  1280 + if ( moveDiffX <= 0 ) {
  1281 + // Case: IV.
  1282 + if ( moveDiffY <= 0 ) {
  1283 + if ( moveRatio >= ratio )
  1284 + adjustToY();
  1285 + else
  1286 + adjustToX();
  1287 + }
  1288 +
  1289 + // Case: I.
  1290 + else {
  1291 + adjustToY();
  1292 + }
  1293 + } else {
  1294 + // Case: III.
  1295 + if ( moveDiffY <= 0 )
  1296 + adjustToX();
  1297 +
  1298 + // Case: II.
  1299 + else {
  1300 + if ( moveRatio >= ratio ) {
  1301 + adjustToX();
  1302 + } else {
  1303 + adjustToY();
  1304 + }
  1305 + }
  1306 + }
  1307 + }
  1308 +
  1309 + // Don't update attributes if less than 10.
  1310 + // This is to prevent images to visually disappear.
  1311 + if ( newWidth >= 15 && newHeight >= 15 ) {
  1312 + image.setAttributes( { width: newWidth, height: newHeight } );
  1313 + updateData = true;
  1314 + } else {
  1315 + updateData = false;
  1316 + }
  1317 + }
  1318 +
  1319 + function onMouseUp() {
  1320 + var l;
  1321 +
  1322 + while ( ( l = listeners.pop() ) )
  1323 + l.removeListener();
  1324 +
  1325 + // Restore default cursor by removing special class.
  1326 + editable.removeClass( cursorClass );
  1327 +
  1328 + // This is to bring back the regular behaviour of the resizer.
  1329 + resizer.removeClass( 'cke_image_resizing' );
  1330 +
  1331 + if ( updateData ) {
  1332 + widget.setData( { width: newWidth, height: newHeight } );
  1333 +
  1334 + // Save another undo snapshot: after resizing.
  1335 + editor.fire( 'saveSnapshot' );
  1336 + }
  1337 +
  1338 + // Don't update data twice or more.
  1339 + updateData = false;
  1340 + }
  1341 + } );
  1342 +
  1343 + // Change the position of the widget resizer when data changes.
  1344 + widget.on( 'data', function() {
  1345 + resizer[ widget.data.align == 'right' ? 'addClass' : 'removeClass' ]( 'cke_image_resizer_left' );
  1346 + } );
  1347 + }
  1348 +
  1349 + // Integrates widget alignment setting with justify
  1350 + // plugin's commands (execution and refreshment).
  1351 + // @param {CKEDITOR.editor} editor
  1352 + // @param {String} value 'left', 'right', 'center' or 'block'
  1353 + function alignCommandIntegrator( editor ) {
  1354 + var execCallbacks = [],
  1355 + enabled;
  1356 +
  1357 + return function( value ) {
  1358 + var command = editor.getCommand( 'justify' + value );
  1359 +
  1360 + // Most likely, the justify plugin isn't loaded.
  1361 + if ( !command )
  1362 + return;
  1363 +
  1364 + // This command will be manually refreshed along with
  1365 + // other commands after exec.
  1366 + execCallbacks.push( function() {
  1367 + command.refresh( editor, editor.elementPath() );
  1368 + } );
  1369 +
  1370 + if ( value in { right: 1, left: 1, center: 1 } ) {
  1371 + command.on( 'exec', function( evt ) {
  1372 + var widget = getFocusedWidget( editor );
  1373 +
  1374 + if ( widget ) {
  1375 + widget.setData( 'align', value );
  1376 +
  1377 + // Once the widget changed its align, all the align commands
  1378 + // must be refreshed: the event is to be cancelled.
  1379 + for ( var i = execCallbacks.length; i--; )
  1380 + execCallbacks[ i ]();
  1381 +
  1382 + evt.cancel();
  1383 + }
  1384 + } );
  1385 + }
  1386 +
  1387 + command.on( 'refresh', function( evt ) {
  1388 + var widget = getFocusedWidget( editor ),
  1389 + allowed = { right: 1, left: 1, center: 1 };
  1390 +
  1391 + if ( !widget )
  1392 + return;
  1393 +
  1394 + // Cache "enabled" on first use. This is because filter#checkFeature may
  1395 + // not be available during plugin's afterInit in the future — a moment when
  1396 + // alignCommandIntegrator is called.
  1397 + if ( enabled === undefined )
  1398 + enabled = editor.filter.checkFeature( editor.widgets.registered.image.features.align );
  1399 +
  1400 + // Don't allow justify commands when widget alignment is disabled (http://dev.ckeditor.com/ticket/11004).
  1401 + if ( !enabled )
  1402 + this.setState( CKEDITOR.TRISTATE_DISABLED );
  1403 + else {
  1404 + this.setState(
  1405 + ( widget.data.align == value ) ? (
  1406 + CKEDITOR.TRISTATE_ON
  1407 + ) : (
  1408 + ( value in allowed ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED
  1409 + )
  1410 + );
  1411 + }
  1412 +
  1413 + evt.cancel();
  1414 + } );
  1415 + };
  1416 + }
  1417 +
  1418 + function linkCommandIntegrator( editor ) {
  1419 + // Nothing to integrate with if link is not loaded.
  1420 + if ( !editor.plugins.link )
  1421 + return;
  1422 +
  1423 + CKEDITOR.on( 'dialogDefinition', function( evt ) {
  1424 + var dialog = evt.data;
  1425 +
  1426 + if ( dialog.name == 'link' ) {
  1427 + var def = dialog.definition;
  1428 +
  1429 + var onShow = def.onShow,
  1430 + onOk = def.onOk;
  1431 +
  1432 + def.onShow = function() {
  1433 + var widget = getFocusedWidget( editor ),
  1434 + displayTextField = this.getContentElement( 'info', 'linkDisplayText' ).getElement().getParent().getParent();
  1435 +
  1436 + // Widget cannot be enclosed in a link, i.e.
  1437 + // <a>foo<inline widget/>bar</a>
  1438 + if ( widget && ( widget.inline ? !widget.wrapper.getAscendant( 'a' ) : 1 ) ) {
  1439 + this.setupContent( widget.data.link || {} );
  1440 +
  1441 + // Hide the display text in case of linking image2 widget.
  1442 + displayTextField.hide();
  1443 + } else {
  1444 + // Make sure that display text is visible, as it might be hidden by image2 integration
  1445 + // before.
  1446 + displayTextField.show();
  1447 + onShow.apply( this, arguments );
  1448 + }
  1449 + };
  1450 +
  1451 + // Set widget data if linking the widget using
  1452 + // link dialog (instead of default action).
  1453 + // State shifter handles data change and takes
  1454 + // care of internal DOM structure of linked widget.
  1455 + def.onOk = function() {
  1456 + var widget = getFocusedWidget( editor );
  1457 +
  1458 + // Widget cannot be enclosed in a link, i.e.
  1459 + // <a>foo<inline widget/>bar</a>
  1460 + if ( widget && ( widget.inline ? !widget.wrapper.getAscendant( 'a' ) : 1 ) ) {
  1461 + var data = {};
  1462 +
  1463 + // Collect data from fields.
  1464 + this.commitContent( data );
  1465 +
  1466 + // Set collected data to widget.
  1467 + widget.setData( 'link', data );
  1468 + } else {
  1469 + onOk.apply( this, arguments );
  1470 + }
  1471 + };
  1472 + }
  1473 + } );
  1474 +
  1475 + // Overwrite default behaviour of unlink command.
  1476 + editor.getCommand( 'unlink' ).on( 'exec', function( evt ) {
  1477 + var widget = getFocusedWidget( editor );
  1478 +
  1479 + // Override unlink only when link truly belongs to the widget.
  1480 + // If wrapped inline widget in a link, let default unlink work (http://dev.ckeditor.com/ticket/11814).
  1481 + if ( !widget || !widget.parts.link )
  1482 + return;
  1483 +
  1484 + widget.setData( 'link', null );
  1485 +
  1486 + // Selection (which is fake) may not change if unlinked image in focused widget,
  1487 + // i.e. if captioned image. Let's refresh command state manually here.
  1488 + this.refresh( editor, editor.elementPath() );
  1489 +
  1490 + evt.cancel();
  1491 + } );
  1492 +
  1493 + // Overwrite default refresh of unlink command.
  1494 + editor.getCommand( 'unlink' ).on( 'refresh', function( evt ) {
  1495 + var widget = getFocusedWidget( editor );
  1496 +
  1497 + if ( !widget )
  1498 + return;
  1499 +
  1500 + // Note that widget may be wrapped in a link, which
  1501 + // does not belong to that widget (http://dev.ckeditor.com/ticket/11814).
  1502 + this.setState( widget.data.link || widget.wrapper.getAscendant( 'a' ) ?
  1503 + CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
  1504 +
  1505 + evt.cancel();
  1506 + } );
  1507 + }
  1508 +
  1509 + // Returns the focused widget, if of the type specific for this plugin.
  1510 + // If no widget is focused, `null` is returned.
  1511 + //
  1512 + // @param {CKEDITOR.editor}
  1513 + // @returns {CKEDITOR.plugins.widget}
  1514 + function getFocusedWidget( editor ) {
  1515 + var widget = editor.widgets.focused;
  1516 +
  1517 + if ( widget && widget.name == 'image' )
  1518 + return widget;
  1519 +
  1520 + return null;
  1521 + }
  1522 +
  1523 + // Returns a set of widget allowedContent rules, depending
  1524 + // on configurations like config#image2_alignClasses or
  1525 + // config#image2_captionedClass.
  1526 + //
  1527 + // @param {CKEDITOR.editor}
  1528 + // @returns {Object}
  1529 + function getWidgetAllowedContent( editor ) {
  1530 + var alignClasses = editor.config.image2_alignClasses,
  1531 + rules = {
  1532 + // Widget may need <div> or <p> centering wrapper.
  1533 + div: {
  1534 + match: centerWrapperChecker( editor )
  1535 + },
  1536 + p: {
  1537 + match: centerWrapperChecker( editor )
  1538 + },
  1539 + img: {
  1540 + attributes: '!src,alt,width,height'
  1541 + },
  1542 + figure: {
  1543 + classes: '!' + editor.config.image2_captionedClass
  1544 + },
  1545 + figcaption: true
  1546 + };
  1547 +
  1548 + if ( alignClasses ) {
  1549 + // Centering class from the config.
  1550 + rules.div.classes = alignClasses[ 1 ];
  1551 + rules.p.classes = rules.div.classes;
  1552 +
  1553 + // Left/right classes from the config.
  1554 + rules.img.classes = alignClasses[ 0 ] + ',' + alignClasses[ 2 ];
  1555 + rules.figure.classes += ',' + rules.img.classes;
  1556 + } else {
  1557 + // Centering with text-align.
  1558 + rules.div.styles = 'text-align';
  1559 + rules.p.styles = 'text-align';
  1560 +
  1561 + rules.img.styles = 'float';
  1562 + rules.figure.styles = 'float,display';
  1563 + }
  1564 +
  1565 + return rules;
  1566 + }
  1567 +
  1568 + // Returns a set of widget feature rules, depending
  1569 + // on editor configuration. Note that the following may not cover
  1570 + // all the possible cases since requiredContent supports a single
  1571 + // tag only.
  1572 + //
  1573 + // @param {CKEDITOR.editor}
  1574 + // @returns {Object}
  1575 + function getWidgetFeatures( editor ) {
  1576 + var alignClasses = editor.config.image2_alignClasses,
  1577 + features = {
  1578 + dimension: {
  1579 + requiredContent: 'img[width,height]'
  1580 + },
  1581 + align: {
  1582 + requiredContent: 'img' +
  1583 + ( alignClasses ? '(' + alignClasses[ 0 ] + ')' : '{float}' )
  1584 + },
  1585 + caption: {
  1586 + requiredContent: 'figcaption'
  1587 + }
  1588 + };
  1589 +
  1590 + return features;
  1591 + }
  1592 +
  1593 + // Returns element which is styled, considering current
  1594 + // state of the widget.
  1595 + //
  1596 + // @see CKEDITOR.plugins.widget#applyStyle
  1597 + // @param {CKEDITOR.plugins.widget} widget
  1598 + // @returns {CKEDITOR.dom.element}
  1599 + function getStyleableElement( widget ) {
  1600 + return widget.data.hasCaption ? widget.element : widget.parts.image;
  1601 + }
  1602 +} )();
  1603 +
  1604 +/**
  1605 + * A CSS class applied to the `<figure>` element of a captioned image.
  1606 + *
  1607 + * Read more in the [documentation](#!/guide/dev_captionedimage) and see the
  1608 + * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html).
  1609 + *
  1610 + * // Changes the class to "captionedImage".
  1611 + * config.image2_captionedClass = 'captionedImage';
  1612 + *
  1613 + * @cfg {String} [image2_captionedClass='image']
  1614 + * @member CKEDITOR.config
  1615 + */
  1616 +CKEDITOR.config.image2_captionedClass = 'image';
  1617 +
  1618 +/**
  1619 + * Determines whether dimension inputs should be automatically filled when the image URL changes in the Enhanced Image
  1620 + * plugin dialog window.
  1621 + *
  1622 + * Read more in the [documentation](#!/guide/dev_captionedimage) and see the
  1623 + * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html).
  1624 + *
  1625 + * config.image2_prefillDimensions = false;
  1626 + *
  1627 + * @since 4.5
  1628 + * @cfg {Boolean} [image2_prefillDimensions=true]
  1629 + * @member CKEDITOR.config
  1630 + */
  1631 +
  1632 +/**
  1633 + * Disables the image resizer. By default the resizer is enabled.
  1634 + *
  1635 + * Read more in the [documentation](#!/guide/dev_captionedimage) and see the
  1636 + * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html).
  1637 + *
  1638 + * config.image2_disableResizer = true;
  1639 + *
  1640 + * @since 4.5
  1641 + * @cfg {Boolean} [image2_disableResizer=false]
  1642 + * @member CKEDITOR.config
  1643 + */
  1644 +
  1645 +/**
  1646 + * CSS classes applied to aligned images. Useful to take control over the way
  1647 + * the images are aligned, i.e. to customize output HTML and integrate external stylesheets.
  1648 + *
  1649 + * Classes should be defined in an array of three elements, containing left, center, and right
  1650 + * alignment classes, respectively. For example:
  1651 + *
  1652 + * config.image2_alignClasses = [ 'align-left', 'align-center', 'align-right' ];
  1653 + *
  1654 + * **Note**: Once this configuration option is set, the plugin will no longer produce inline
  1655 + * styles for alignment. It means that e.g. the following HTML will be produced:
  1656 + *
  1657 + * <img alt="My image" class="custom-center-class" src="foo.png" />
  1658 + *
  1659 + * instead of:
  1660 + *
  1661 + * <img alt="My image" style="float:left" src="foo.png" />
  1662 + *
  1663 + * **Note**: Once this configuration option is set, corresponding style definitions
  1664 + * must be supplied to the editor:
  1665 + *
  1666 + * * For [classic editor](#!/guide/dev_framed) it can be done by defining additional
  1667 + * styles in the {@link CKEDITOR.config#contentsCss stylesheets loaded by the editor}. The same
  1668 + * styles must be provided on the target page where the content will be loaded.
  1669 + * * For [inline editor](#!/guide/dev_inline) the styles can be defined directly
  1670 + * with `<style> ... <style>` or `<link href="..." rel="stylesheet">`, i.e. within the `<head>`
  1671 + * of the page.
  1672 + *
  1673 + * For example, considering the following configuration:
  1674 + *
  1675 + * config.image2_alignClasses = [ 'align-left', 'align-center', 'align-right' ];
  1676 + *
  1677 + * CSS rules can be defined as follows:
  1678 + *
  1679 + * .align-left {
  1680 + * float: left;
  1681 + * }
  1682 + *
  1683 + * .align-right {
  1684 + * float: right;
  1685 + * }
  1686 + *
  1687 + * .align-center {
  1688 + * text-align: center;
  1689 + * }
  1690 + *
  1691 + * .align-center > figure {
  1692 + * display: inline-block;
  1693 + * }
  1694 + *
  1695 + * Read more in the [documentation](#!/guide/dev_captionedimage) and see the
  1696 + * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html).
  1697 + *
  1698 + * @since 4.4
  1699 + * @cfg {String[]} [image2_alignClasses=null]
  1700 + * @member CKEDITOR.config
  1701 + */
  1702 +
  1703 +/**
  1704 + * Determines whether alternative text is required for the captioned image.
  1705 + *
  1706 + * config.image2_altRequired = true;
  1707 + *
  1708 + * Read more in the [documentation](#!/guide/dev_captionedimage) and see the
  1709 + * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html).
  1710 + *
  1711 + * @since 4.6.0
  1712 + * @cfg {Boolean} [image2_altRequired=false]
  1713 + * @member CKEDITOR.config
  1714 + */
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +
  6 +'use strict';
  7 +
  8 +( function() {
  9 +
  10 + var template = '<img alt="" src="" />',
  11 + templateBlock = new CKEDITOR.template(
  12 + '<figure class="{captionedClass}">' +
  13 + template +
  14 + '<figcaption>{captionPlaceholder}</figcaption>' +
  15 + '</figure>' ),
  16 + alignmentsObj = { left: 0, center: 1, right: 2 },
  17 + regexPercent = /^\s*(\d+\%)\s*$/i;
  18 +
  19 + CKEDITOR.plugins.add( 'image2', {
  20 + // jscs:disable maximumLineLength
  21 + lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,es-mx,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE%
  22 + // jscs:enable maximumLineLength
  23 + requires: 'widget,dialog',
  24 + icons: 'image',
  25 + hidpi: true,
  26 +
  27 + onLoad: function() {
  28 + CKEDITOR.addCss(
  29 + '.cke_image_nocaption{' +
  30 + // This is to remove unwanted space so resize
  31 + // wrapper is displayed property.
  32 + 'line-height:0' +
  33 + '}' +
  34 + '.cke_editable.cke_image_sw, .cke_editable.cke_image_sw *{cursor:sw-resize !important}' +
  35 + '.cke_editable.cke_image_se, .cke_editable.cke_image_se *{cursor:se-resize !important}' +
  36 + '.cke_image_resizer{' +
  37 + 'display:none;' +
  38 + 'position:absolute;' +
  39 + 'width:10px;' +
  40 + 'height:10px;' +
  41 + 'bottom:-5px;' +
  42 + 'right:-5px;' +
  43 + 'background:#000;' +
  44 + 'outline:1px solid #fff;' +
  45 + // Prevent drag handler from being misplaced (http://dev.ckeditor.com/ticket/11207).
  46 + 'line-height:0;' +
  47 + 'cursor:se-resize;' +
  48 + '}' +
  49 + '.cke_image_resizer_wrapper{' +
  50 + 'position:relative;' +
  51 + 'display:inline-block;' +
  52 + 'line-height:0;' +
  53 + '}' +
  54 + // Bottom-left corner style of the resizer.
  55 + '.cke_image_resizer.cke_image_resizer_left{' +
  56 + 'right:auto;' +
  57 + 'left:-5px;' +
  58 + 'cursor:sw-resize;' +
  59 + '}' +
  60 + '.cke_widget_wrapper:hover .cke_image_resizer,' +
  61 + '.cke_image_resizer.cke_image_resizing{' +
  62 + 'display:block' +
  63 + '}' +
  64 + // Expand widget wrapper when linked inline image.
  65 + '.cke_widget_wrapper>a{' +
  66 + 'display:inline-block' +
  67 + '}' );
  68 + },
  69 +
  70 + init: function( editor ) {
  71 + // Adapts configuration from original image plugin. Should be removed
  72 + // when we'll rename image2 to image.
  73 + var config = editor.config,
  74 + lang = editor.lang.image2,
  75 + image = widgetDef( editor );
  76 +
  77 + // Since filebrowser plugin discovers config properties by dialog (plugin?)
  78 + // names (sic!), this hack will be necessary as long as Image2 is not named
  79 + // Image. And since Image2 will never be Image, for sure some filebrowser logic
  80 + // got to be refined.
  81 + config.filebrowserImage2BrowseUrl = config.filebrowserImageBrowseUrl;
  82 + config.filebrowserImage2UploadUrl = config.filebrowserImageUploadUrl;
  83 +
  84 + // Add custom elementspath names to widget definition.
  85 + image.pathName = lang.pathName;
  86 + image.editables.caption.pathName = lang.pathNameCaption;
  87 +
  88 + // Register the widget.
  89 + editor.widgets.add( 'image', image );
  90 +
  91 + // Add toolbar button for this plugin.
  92 + editor.ui.addButton && editor.ui.addButton( 'Image', {
  93 + label: editor.lang.common.image,
  94 + command: 'image',
  95 + toolbar: 'insert,10'
  96 + } );
  97 +
  98 + // Register context menu option for editing widget.
  99 + if ( editor.contextMenu ) {
  100 + editor.addMenuGroup( 'image', 10 );
  101 +
  102 + editor.addMenuItem( 'image', {
  103 + label: lang.menu,
  104 + command: 'image',
  105 + group: 'image'
  106 + } );
  107 + }
  108 +
  109 + CKEDITOR.dialog.add( 'image2', this.path + 'dialogs/image2.js' );
  110 + },
  111 +
  112 + afterInit: function( editor ) {
  113 + // Integrate with align commands (justify plugin).
  114 + var align = { left: 1, right: 1, center: 1, block: 1 },
  115 + integrate = alignCommandIntegrator( editor );
  116 +
  117 + for ( var value in align )
  118 + integrate( value );
  119 +
  120 + // Integrate with link commands (link plugin).
  121 + linkCommandIntegrator( editor );
  122 + }
  123 + } );
  124 +
  125 + // Wiget states (forms) depending on alignment and configuration.
  126 + //
  127 + // Non-captioned widget (inline styles)
  128 + // ┌──────┬───────────────────────────────┬─────────────────────────────┐
  129 + // │Align │Internal form │Data │
  130 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  131 + // │none │<wrapper> │<img /> │
  132 + // │ │ <img /> │ │
  133 + // │ │</wrapper> │ │
  134 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  135 + // │left │<wrapper style=”float:left”> │<img style=”float:left” /> │
  136 + // │ │ <img /> │ │
  137 + // │ │</wrapper> │ │
  138 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  139 + // │center│<wrapper> │<p style=”text-align:center”>│
  140 + // │ │ <p style=”text-align:center”> │ <img /> │
  141 + // │ │ <img /> │</p> │
  142 + // │ │ </p> │ │
  143 + // │ │</wrapper> │ │
  144 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  145 + // │right │<wrapper style=”float:right”> │<img style=”float:right” /> │
  146 + // │ │ <img /> │ │
  147 + // │ │</wrapper> │ │
  148 + // └──────┴───────────────────────────────┴─────────────────────────────┘
  149 + //
  150 + // Non-captioned widget (config.image2_alignClasses defined)
  151 + // ┌──────┬───────────────────────────────┬─────────────────────────────┐
  152 + // │Align │Internal form │Data │
  153 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  154 + // │none │<wrapper> │<img /> │
  155 + // │ │ <img /> │ │
  156 + // │ │</wrapper> │ │
  157 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  158 + // │left │<wrapper class=”left”> │<img class=”left” /> │
  159 + // │ │ <img /> │ │
  160 + // │ │</wrapper> │ │
  161 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  162 + // │center│<wrapper> │<p class=”center”> │
  163 + // │ │ <p class=”center”> │ <img /> │
  164 + // │ │ <img /> │</p> │
  165 + // │ │ </p> │ │
  166 + // │ │</wrapper> │ │
  167 + // ├──────┼───────────────────────────────┼─────────────────────────────┤
  168 + // │right │<wrapper class=”right”> │<img class=”right” /> │
  169 + // │ │ <img /> │ │
  170 + // │ │</wrapper> │ │
  171 + // └──────┴───────────────────────────────┴─────────────────────────────┘
  172 + //
  173 + // Captioned widget (inline styles)
  174 + // ┌──────┬────────────────────────────────────────┬────────────────────────────────────────┐
  175 + // │Align │Internal form │Data │
  176 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  177 + // │none │<wrapper> │<figure /> │
  178 + // │ │ <figure /> │ │
  179 + // │ │</wrapper> │ │
  180 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  181 + // │left │<wrapper style=”float:left”> │<figure style=”float:left” /> │
  182 + // │ │ <figure /> │ │
  183 + // │ │</wrapper> │ │
  184 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  185 + // │center│<wrapper style=”text-align:center”> │<div style=”text-align:center”> │
  186 + // │ │ <figure style=”display:inline-block” />│ <figure style=”display:inline-block” />│
  187 + // │ │</wrapper> │</p> │
  188 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  189 + // │right │<wrapper style=”float:right”> │<figure style=”float:right” /> │
  190 + // │ │ <figure /> │ │
  191 + // │ │</wrapper> │ │
  192 + // └──────┴────────────────────────────────────────┴────────────────────────────────────────┘
  193 + //
  194 + // Captioned widget (config.image2_alignClasses defined)
  195 + // ┌──────┬────────────────────────────────────────┬────────────────────────────────────────┐
  196 + // │Align │Internal form │Data │
  197 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  198 + // │none │<wrapper> │<figure /> │
  199 + // │ │ <figure /> │ │
  200 + // │ │</wrapper> │ │
  201 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  202 + // │left │<wrapper class=”left”> │<figure class=”left” /> │
  203 + // │ │ <figure /> │ │
  204 + // │ │</wrapper> │ │
  205 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  206 + // │center│<wrapper class=”center”> │<div class=”center”> │
  207 + // │ │ <figure /> │ <figure /> │
  208 + // │ │</wrapper> │</p> │
  209 + // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤
  210 + // │right │<wrapper class=”right”> │<figure class=”right” /> │
  211 + // │ │ <figure /> │ │
  212 + // │ │</wrapper> │ │
  213 + // └──────┴────────────────────────────────────────┴────────────────────────────────────────┘
  214 + //
  215 + // @param {CKEDITOR.editor}
  216 + // @returns {Object}
  217 + function widgetDef( editor ) {
  218 + var alignClasses = editor.config.image2_alignClasses,
  219 + captionedClass = editor.config.image2_captionedClass;
  220 +
  221 + function deflate() {
  222 + if ( this.deflated )
  223 + return;
  224 +
  225 + // Remember whether widget was focused before destroyed.
  226 + if ( editor.widgets.focused == this.widget )
  227 + this.focused = true;
  228 +
  229 + editor.widgets.destroy( this.widget );
  230 +
  231 + // Mark widget was destroyed.
  232 + this.deflated = true;
  233 + }
  234 +
  235 + function inflate() {
  236 + var editable = editor.editable(),
  237 + doc = editor.document;
  238 +
  239 + // Create a new widget. This widget will be either captioned
  240 + // non-captioned, block or inline according to what is the
  241 + // new state of the widget.
  242 + if ( this.deflated ) {
  243 + this.widget = editor.widgets.initOn( this.element, 'image', this.widget.data );
  244 +
  245 + // Once widget was re-created, it may become an inline element without
  246 + // block wrapper (i.e. when unaligned, end not captioned). Let's do some
  247 + // sort of autoparagraphing here (http://dev.ckeditor.com/ticket/10853).
  248 + if ( this.widget.inline && !( new CKEDITOR.dom.elementPath( this.widget.wrapper, editable ).block ) ) {
  249 + var block = doc.createElement( editor.activeEnterMode == CKEDITOR.ENTER_P ? 'p' : 'div' );
  250 + block.replace( this.widget.wrapper );
  251 + this.widget.wrapper.move( block );
  252 + }
  253 +
  254 + // The focus must be transferred from the old one (destroyed)
  255 + // to the new one (just created).
  256 + if ( this.focused ) {
  257 + this.widget.focus();
  258 + delete this.focused;
  259 + }
  260 +
  261 + delete this.deflated;
  262 + }
  263 +
  264 + // If now widget was destroyed just update wrapper's alignment.
  265 + // According to the new state.
  266 + else {
  267 + setWrapperAlign( this.widget, alignClasses );
  268 + }
  269 + }
  270 +
  271 + return {
  272 + allowedContent: getWidgetAllowedContent( editor ),
  273 +
  274 + requiredContent: 'img[src,alt]',
  275 +
  276 + features: getWidgetFeatures( editor ),
  277 +
  278 + styleableElements: 'img figure',
  279 +
  280 + // This widget converts style-driven dimensions to attributes.
  281 + contentTransformations: [
  282 + [ 'img[width]: sizeToAttribute' ]
  283 + ],
  284 +
  285 + // This widget has an editable caption.
  286 + editables: {
  287 + caption: {
  288 + selector: 'figcaption',
  289 + allowedContent: 'br em strong sub sup u s; a[!href,target]'
  290 + }
  291 + },
  292 +
  293 + parts: {
  294 + image: 'img',
  295 + caption: 'figcaption'
  296 + // parts#link defined in widget#init
  297 + },
  298 +
  299 + // The name of this widget's dialog.
  300 + dialog: 'image2',
  301 +
  302 + // Template of the widget: plain image.
  303 + template: template,
  304 +
  305 + data: function() {
  306 + var features = this.features;
  307 +
  308 + // Image can't be captioned when figcaption is disallowed (http://dev.ckeditor.com/ticket/11004).
  309 + if ( this.data.hasCaption && !editor.filter.checkFeature( features.caption ) )
  310 + this.data.hasCaption = false;
  311 +
  312 + // Image can't be aligned when floating is disallowed (http://dev.ckeditor.com/ticket/11004).
  313 + if ( this.data.align != 'none' && !editor.filter.checkFeature( features.align ) )
  314 + this.data.align = 'none';
  315 +
  316 + // Convert the internal form of the widget from the old state to the new one.
  317 + this.shiftState( {
  318 + widget: this,
  319 + element: this.element,
  320 + oldData: this.oldData,
  321 + newData: this.data,
  322 + deflate: deflate,
  323 + inflate: inflate
  324 + } );
  325 +
  326 + // Update widget.parts.link since it will not auto-update unless widget
  327 + // is destroyed and re-inited.
  328 + if ( !this.data.link ) {
  329 + if ( this.parts.link )
  330 + delete this.parts.link;
  331 + } else {
  332 + if ( !this.parts.link )
  333 + this.parts.link = this.parts.image.getParent();
  334 + }
  335 +
  336 + this.parts.image.setAttributes( {
  337 + src: this.data.src,
  338 +
  339 + // This internal is required by the editor.
  340 + 'data-cke-saved-src': this.data.src,
  341 +
  342 + alt: this.data.alt
  343 + } );
  344 +
  345 + // If shifting non-captioned -> captioned, remove classes
  346 + // related to styles from <img/>.
  347 + if ( this.oldData && !this.oldData.hasCaption && this.data.hasCaption ) {
  348 + for ( var c in this.data.classes )
  349 + this.parts.image.removeClass( c );
  350 + }
  351 +
  352 + // Set dimensions of the image according to gathered data.
  353 + // Do it only when the attributes are allowed (http://dev.ckeditor.com/ticket/11004).
  354 + if ( editor.filter.checkFeature( features.dimension ) )
  355 + setDimensions( this );
  356 +
  357 + // Cache current data.
  358 + this.oldData = CKEDITOR.tools.extend( {}, this.data );
  359 + },
  360 +
  361 + init: function() {
  362 + var helpers = CKEDITOR.plugins.image2,
  363 + image = this.parts.image,
  364 + data = {
  365 + hasCaption: !!this.parts.caption,
  366 + src: image.getAttribute( 'src' ),
  367 + alt: image.getAttribute( 'alt' ) || '',
  368 + width: image.getAttribute( 'width' ) || '',
  369 + height: image.getAttribute( 'height' ) || '',
  370 +
  371 + // Lock ratio is on by default (http://dev.ckeditor.com/ticket/10833).
  372 + lock: this.ready ? helpers.checkHasNaturalRatio( image ) : true
  373 + };
  374 +
  375 + // If we used 'a' in widget#parts definition, it could happen that
  376 + // selected element is a child of widget.parts#caption. Since there's no clever
  377 + // way to solve it with CSS selectors, it's done like that. (http://dev.ckeditor.com/ticket/11783).
  378 + var link = image.getAscendant( 'a' );
  379 +
  380 + if ( link && this.wrapper.contains( link ) )
  381 + this.parts.link = link;
  382 +
  383 + // Depending on configuration, read style/class from element and
  384 + // then remove it. Removed style/class will be set on wrapper in #data listener.
  385 + // Note: Center alignment is detected during upcast, so only left/right cases
  386 + // are checked below.
  387 + if ( !data.align ) {
  388 + var alignElement = data.hasCaption ? this.element : image;
  389 +
  390 + // Read the initial left/right alignment from the class set on element.
  391 + if ( alignClasses ) {
  392 + if ( alignElement.hasClass( alignClasses[ 0 ] ) ) {
  393 + data.align = 'left';
  394 + } else if ( alignElement.hasClass( alignClasses[ 2 ] ) ) {
  395 + data.align = 'right';
  396 + }
  397 +
  398 + if ( data.align ) {
  399 + alignElement.removeClass( alignClasses[ alignmentsObj[ data.align ] ] );
  400 + } else {
  401 + data.align = 'none';
  402 + }
  403 + }
  404 + // Read initial float style from figure/image and then remove it.
  405 + else {
  406 + data.align = alignElement.getStyle( 'float' ) || 'none';
  407 + alignElement.removeStyle( 'float' );
  408 + }
  409 + }
  410 +
  411 + // Update data.link object with attributes if the link has been discovered.
  412 + if ( editor.plugins.link && this.parts.link ) {
  413 + data.link = helpers.getLinkAttributesParser()( editor, this.parts.link );
  414 +
  415 + // Get rid of cke_widget_* classes in data. Otherwise
  416 + // they might appear in link dialog.
  417 + var advanced = data.link.advanced;
  418 + if ( advanced && advanced.advCSSClasses ) {
  419 + advanced.advCSSClasses = CKEDITOR.tools.trim( advanced.advCSSClasses.replace( /cke_\S+/, '' ) );
  420 + }
  421 + }
  422 +
  423 + // Get rid of extra vertical space when there's no caption.
  424 + // It will improve the look of the resizer.
  425 + this.wrapper[ ( data.hasCaption ? 'remove' : 'add' ) + 'Class' ]( 'cke_image_nocaption' );
  426 +
  427 + this.setData( data );
  428 +
  429 + // Setup dynamic image resizing with mouse.
  430 + // Don't initialize resizer when dimensions are disallowed (http://dev.ckeditor.com/ticket/11004).
  431 + // Don't initialize resizer when editor.readOnly is set to true (#719).
  432 + if ( editor.filter.checkFeature( this.features.dimension ) && editor.config.image2_disableResizer !== true && editor.readOnly != true ) {
  433 + setupResizer( this );
  434 + }
  435 +
  436 + this.shiftState = helpers.stateShifter( this.editor );
  437 +
  438 + // Add widget editing option to its context menu.
  439 + this.on( 'contextMenu', function( evt ) {
  440 + evt.data.image = CKEDITOR.TRISTATE_OFF;
  441 +
  442 + // Integrate context menu items for link.
  443 + // Note that widget may be wrapped in a link, which
  444 + // does not belong to that widget (http://dev.ckeditor.com/ticket/11814).
  445 + if ( this.parts.link || this.wrapper.getAscendant( 'a' ) )
  446 + evt.data.link = evt.data.unlink = CKEDITOR.TRISTATE_OFF;
  447 + } );
  448 +
  449 + // Pass the reference to this widget to the dialog.
  450 + this.on( 'dialog', function( evt ) {
  451 + evt.data.widget = this;
  452 + }, this );
  453 + },
  454 +
  455 + // Overrides default method to handle internal mutability of Image2.
  456 + // @see CKEDITOR.plugins.widget#addClass
  457 + addClass: function( className ) {
  458 + getStyleableElement( this ).addClass( className );
  459 + },
  460 +
  461 + // Overrides default method to handle internal mutability of Image2.
  462 + // @see CKEDITOR.plugins.widget#hasClass
  463 + hasClass: function( className ) {
  464 + return getStyleableElement( this ).hasClass( className );
  465 + },
  466 +
  467 + // Overrides default method to handle internal mutability of Image2.
  468 + // @see CKEDITOR.plugins.widget#removeClass
  469 + removeClass: function( className ) {
  470 + getStyleableElement( this ).removeClass( className );
  471 + },
  472 +
  473 + // Overrides default method to handle internal mutability of Image2.
  474 + // @see CKEDITOR.plugins.widget#getClasses
  475 + getClasses: ( function() {
  476 + var classRegex = new RegExp( '^(' + [].concat( captionedClass, alignClasses ).join( '|' ) + ')$' );
  477 +
  478 + return function() {
  479 + var classes = this.repository.parseElementClasses( getStyleableElement( this ).getAttribute( 'class' ) );
  480 +
  481 + // Neither config.image2_captionedClass nor config.image2_alignClasses
  482 + // do not belong to style classes.
  483 + for ( var c in classes ) {
  484 + if ( classRegex.test( c ) )
  485 + delete classes[ c ];
  486 + }
  487 +
  488 + return classes;
  489 + };
  490 + } )(),
  491 +
  492 + upcast: upcastWidgetElement( editor ),
  493 + downcast: downcastWidgetElement( editor ),
  494 +
  495 + getLabel: function() {
  496 + var label = ( this.data.alt || '' ) + ' ' + this.pathName;
  497 +
  498 + return this.editor.lang.widget.label.replace( /%1/, label );
  499 + }
  500 + };
  501 + }
  502 +
  503 + /**
  504 + * A set of Enhanced Image (image2) plugin helpers.
  505 + *
  506 + * @class
  507 + * @singleton
  508 + */
  509 + CKEDITOR.plugins.image2 = {
  510 + stateShifter: function( editor ) {
  511 + // Tag name used for centering non-captioned widgets.
  512 + var doc = editor.document,
  513 + alignClasses = editor.config.image2_alignClasses,
  514 + captionedClass = editor.config.image2_captionedClass,
  515 + editable = editor.editable(),
  516 +
  517 + // The order that stateActions get executed. It matters!
  518 + shiftables = [ 'hasCaption', 'align', 'link' ];
  519 +
  520 + // Atomic procedures, one per state variable.
  521 + var stateActions = {
  522 + align: function( shift, oldValue, newValue ) {
  523 + var el = shift.element;
  524 +
  525 + // Alignment changed.
  526 + if ( shift.changed.align ) {
  527 + // No caption in the new state.
  528 + if ( !shift.newData.hasCaption ) {
  529 + // Changed to "center" (non-captioned).
  530 + if ( newValue == 'center' ) {
  531 + shift.deflate();
  532 + shift.element = wrapInCentering( editor, el );
  533 + }
  534 +
  535 + // Changed to "non-center" from "center" while caption removed.
  536 + if ( !shift.changed.hasCaption && oldValue == 'center' && newValue != 'center' ) {
  537 + shift.deflate();
  538 + shift.element = unwrapFromCentering( el );
  539 + }
  540 + }
  541 + }
  542 +
  543 + // Alignment remains and "center" removed caption.
  544 + else if ( newValue == 'center' && shift.changed.hasCaption && !shift.newData.hasCaption ) {
  545 + shift.deflate();
  546 + shift.element = wrapInCentering( editor, el );
  547 + }
  548 +
  549 + // Finally set display for figure.
  550 + if ( !alignClasses && el.is( 'figure' ) ) {
  551 + if ( newValue == 'center' )
  552 + el.setStyle( 'display', 'inline-block' );
  553 + else
  554 + el.removeStyle( 'display' );
  555 + }
  556 + },
  557 +
  558 + hasCaption: function( shift, oldValue, newValue ) {
  559 + // This action is for real state change only.
  560 + if ( !shift.changed.hasCaption )
  561 + return;
  562 +
  563 + // Get <img/> or <a><img/></a> from widget. Note that widget element might itself
  564 + // be what we're looking for. Also element can be <p style="text-align:center"><a>...</a></p>.
  565 + var imageOrLink;
  566 + if ( shift.element.is( { img: 1, a: 1 } ) )
  567 + imageOrLink = shift.element;
  568 + else
  569 + imageOrLink = shift.element.findOne( 'a,img' );
  570 +
  571 + // Switching hasCaption always destroys the widget.
  572 + shift.deflate();
  573 +
  574 + // There was no caption, but the caption is to be added.
  575 + if ( newValue ) {
  576 + // Create new <figure> from widget template.
  577 + var figure = CKEDITOR.dom.element.createFromHtml( templateBlock.output( {
  578 + captionedClass: captionedClass,
  579 + captionPlaceholder: editor.lang.image2.captionPlaceholder
  580 + } ), doc );
  581 +
  582 + // Replace element with <figure>.
  583 + replaceSafely( figure, shift.element );
  584 +
  585 + // Use old <img/> or <a><img/></a> instead of the one from the template,
  586 + // so we won't lose additional attributes.
  587 + imageOrLink.replace( figure.findOne( 'img' ) );
  588 +
  589 + // Update widget's element.
  590 + shift.element = figure;
  591 + }
  592 +
  593 + // The caption was present, but now it's to be removed.
  594 + else {
  595 + // Unwrap <img/> or <a><img/></a> from figure.
  596 + imageOrLink.replace( shift.element );
  597 +
  598 + // Update widget's element.
  599 + shift.element = imageOrLink;
  600 + }
  601 + },
  602 +
  603 + link: function( shift, oldValue, newValue ) {
  604 + if ( shift.changed.link ) {
  605 + var img = shift.element.is( 'img' ) ?
  606 + shift.element : shift.element.findOne( 'img' ),
  607 + link = shift.element.is( 'a' ) ?
  608 + shift.element : shift.element.findOne( 'a' ),
  609 + // Why deflate:
  610 + // If element is <img/>, it will be wrapped into <a>,
  611 + // which becomes a new widget.element.
  612 + // If element is <a><img/></a>, it will be unlinked
  613 + // so <img/> becomes a new widget.element.
  614 + needsDeflate = ( shift.element.is( 'a' ) && !newValue ) || ( shift.element.is( 'img' ) && newValue ),
  615 + newEl;
  616 +
  617 + if ( needsDeflate )
  618 + shift.deflate();
  619 +
  620 + // If unlinked the image, returned element is <img>.
  621 + if ( !newValue )
  622 + newEl = unwrapFromLink( link );
  623 + else {
  624 + // If linked the image, returned element is <a>.
  625 + if ( !oldValue )
  626 + newEl = wrapInLink( img, shift.newData.link );
  627 +
  628 + // Set and remove all attributes associated with this state.
  629 + var attributes = CKEDITOR.plugins.image2.getLinkAttributesGetter()( editor, newValue );
  630 +
  631 + if ( !CKEDITOR.tools.isEmpty( attributes.set ) )
  632 + ( newEl || link ).setAttributes( attributes.set );
  633 +
  634 + if ( attributes.removed.length )
  635 + ( newEl || link ).removeAttributes( attributes.removed );
  636 + }
  637 +
  638 + if ( needsDeflate )
  639 + shift.element = newEl;
  640 + }
  641 + }
  642 + };
  643 +
  644 + function wrapInCentering( editor, element ) {
  645 + var attribsAndStyles = {};
  646 +
  647 + if ( alignClasses )
  648 + attribsAndStyles.attributes = { 'class': alignClasses[ 1 ] };
  649 + else
  650 + attribsAndStyles.styles = { 'text-align': 'center' };
  651 +
  652 + // There's no gentle way to center inline element with CSS, so create p/div
  653 + // that wraps widget contents and does the trick either with style or class.
  654 + var center = doc.createElement(
  655 + editor.activeEnterMode == CKEDITOR.ENTER_P ? 'p' : 'div', attribsAndStyles );
  656 +
  657 + // Replace element with centering wrapper.
  658 + replaceSafely( center, element );
  659 + element.move( center );
  660 +
  661 + return center;
  662 + }
  663 +
  664 + function unwrapFromCentering( element ) {
  665 + var imageOrLink = element.findOne( 'a,img' );
  666 +
  667 + imageOrLink.replace( element );
  668 +
  669 + return imageOrLink;
  670 + }
  671 +
  672 + // Wraps <img/> -> <a><img/></a>.
  673 + // Returns reference to <a>.
  674 + //
  675 + // @param {CKEDITOR.dom.element} img
  676 + // @param {Object} linkData
  677 + // @returns {CKEDITOR.dom.element}
  678 + function wrapInLink( img, linkData ) {
  679 + var link = doc.createElement( 'a', {
  680 + attributes: {
  681 + href: linkData.url
  682 + }
  683 + } );
  684 +
  685 + link.replace( img );
  686 + img.move( link );
  687 +
  688 + return link;
  689 + }
  690 +
  691 + // De-wraps <a><img/></a> -> <img/>.
  692 + // Returns the reference to <img/>
  693 + //
  694 + // @param {CKEDITOR.dom.element} link
  695 + // @returns {CKEDITOR.dom.element}
  696 + function unwrapFromLink( link ) {
  697 + var img = link.findOne( 'img' );
  698 +
  699 + img.replace( link );
  700 +
  701 + return img;
  702 + }
  703 +
  704 + function replaceSafely( replacing, replaced ) {
  705 + if ( replaced.getParent() ) {
  706 + var range = editor.createRange();
  707 +
  708 + range.moveToPosition( replaced, CKEDITOR.POSITION_BEFORE_START );
  709 +
  710 + // Remove old element. Do it before insertion to avoid a case when
  711 + // element is moved from 'replaced' element before it, what creates
  712 + // a tricky case which insertElementIntorRange does not handle.
  713 + replaced.remove();
  714 +
  715 + editable.insertElementIntoRange( replacing, range );
  716 + }
  717 + else {
  718 + replacing.replace( replaced );
  719 + }
  720 + }
  721 +
  722 + return function( shift ) {
  723 + var name, i;
  724 +
  725 + shift.changed = {};
  726 +
  727 + for ( i = 0; i < shiftables.length; i++ ) {
  728 + name = shiftables[ i ];
  729 +
  730 + shift.changed[ name ] = shift.oldData ?
  731 + shift.oldData[ name ] !== shift.newData[ name ] : false;
  732 + }
  733 +
  734 + // Iterate over possible state variables.
  735 + for ( i = 0; i < shiftables.length; i++ ) {
  736 + name = shiftables[ i ];
  737 +
  738 + stateActions[ name ]( shift,
  739 + shift.oldData ? shift.oldData[ name ] : null,
  740 + shift.newData[ name ] );
  741 + }
  742 +
  743 + shift.inflate();
  744 + };
  745 + },
  746 +
  747 + /**
  748 + * Checks whether the current image ratio matches the natural one
  749 + * by comparing dimensions.
  750 + *
  751 + * @param {CKEDITOR.dom.element} image
  752 + * @returns {Boolean}
  753 + */
  754 + checkHasNaturalRatio: function( image ) {
  755 + var $ = image.$,
  756 + natural = this.getNatural( image );
  757 +
  758 + // The reason for two alternative comparisons is that the rounding can come from
  759 + // both dimensions, e.g. there are two cases:
  760 + // 1. height is computed as a rounded relation of the real height and the value of width,
  761 + // 2. width is computed as a rounded relation of the real width and the value of heigh.
  762 + return Math.round( $.clientWidth / natural.width * natural.height ) == $.clientHeight ||
  763 + Math.round( $.clientHeight / natural.height * natural.width ) == $.clientWidth;
  764 + },
  765 +
  766 + /**
  767 + * Returns natural dimensions of the image. For modern browsers
  768 + * it uses natural(Width|Height). For old ones (IE8) it creates
  769 + * a new image and reads the dimensions.
  770 + *
  771 + * @param {CKEDITOR.dom.element} image
  772 + * @returns {Object}
  773 + */
  774 + getNatural: function( image ) {
  775 + var dimensions;
  776 +
  777 + if ( image.$.naturalWidth ) {
  778 + dimensions = {
  779 + width: image.$.naturalWidth,
  780 + height: image.$.naturalHeight
  781 + };
  782 + } else {
  783 + var img = new Image();
  784 + img.src = image.getAttribute( 'src' );
  785 +
  786 + dimensions = {
  787 + width: img.width,
  788 + height: img.height
  789 + };
  790 + }
  791 +
  792 + return dimensions;
  793 + },
  794 +
  795 + /**
  796 + * Returns an attribute getter function. Default getter comes from the Link plugin
  797 + * and is documented by {@link CKEDITOR.plugins.link#getLinkAttributes}.
  798 + *
  799 + * **Note:** It is possible to override this method and use a custom getter e.g.
  800 + * in the absence of the Link plugin.
  801 + *
  802 + * **Note:** If a custom getter is used, a data model format it produces
  803 + * must be compatible with {@link CKEDITOR.plugins.link#getLinkAttributes}.
  804 + *
  805 + * **Note:** A custom getter must understand the data model format produced by
  806 + * {@link #getLinkAttributesParser} to work correctly.
  807 + *
  808 + * @returns {Function} A function that gets (composes) link attributes.
  809 + * @since 4.5.5
  810 + */
  811 + getLinkAttributesGetter: function() {
  812 + // http://dev.ckeditor.com/ticket/13885
  813 + return CKEDITOR.plugins.link.getLinkAttributes;
  814 + },
  815 +
  816 + /**
  817 + * Returns an attribute parser function. Default parser comes from the Link plugin
  818 + * and is documented by {@link CKEDITOR.plugins.link#parseLinkAttributes}.
  819 + *
  820 + * **Note:** It is possible to override this method and use a custom parser e.g.
  821 + * in the absence of the Link plugin.
  822 + *
  823 + * **Note:** If a custom parser is used, a data model format produced by the parser
  824 + * must be compatible with {@link #getLinkAttributesGetter}.
  825 + *
  826 + * **Note:** If a custom parser is used, it should be compatible with the
  827 + * {@link CKEDITOR.plugins.link#parseLinkAttributes} data model format. Otherwise the
  828 + * Link plugin dialog may not be populated correctly with parsed data. However
  829 + * as long as Enhanced Image is **not** used with the Link plugin dialog, any custom data model
  830 + * will work, being stored as an internal property of Enhanced Image widget's data only.
  831 + *
  832 + * @returns {Function} A function that parses attributes.
  833 + * @since 4.5.5
  834 + */
  835 + getLinkAttributesParser: function() {
  836 + // http://dev.ckeditor.com/ticket/13885
  837 + return CKEDITOR.plugins.link.parseLinkAttributes;
  838 + }
  839 + };
  840 +
  841 + function setWrapperAlign( widget, alignClasses ) {
  842 + var wrapper = widget.wrapper,
  843 + align = widget.data.align,
  844 + hasCaption = widget.data.hasCaption;
  845 +
  846 + if ( alignClasses ) {
  847 + // Remove all align classes first.
  848 + for ( var i = 3; i--; )
  849 + wrapper.removeClass( alignClasses[ i ] );
  850 +
  851 + if ( align == 'center' ) {
  852 + // Avoid touching non-captioned, centered widgets because
  853 + // they have the class set on the element instead of wrapper:
  854 + //
  855 + // <div class="cke_widget_wrapper">
  856 + // <p class="center-class">
  857 + // <img />
  858 + // </p>
  859 + // </div>
  860 + if ( hasCaption ) {
  861 + wrapper.addClass( alignClasses[ 1 ] );
  862 + }
  863 + } else if ( align != 'none' ) {
  864 + wrapper.addClass( alignClasses[ alignmentsObj[ align ] ] );
  865 + }
  866 + } else {
  867 + if ( align == 'center' ) {
  868 + if ( hasCaption )
  869 + wrapper.setStyle( 'text-align', 'center' );
  870 + else
  871 + wrapper.removeStyle( 'text-align' );
  872 +
  873 + wrapper.removeStyle( 'float' );
  874 + }
  875 + else {
  876 + if ( align == 'none' )
  877 + wrapper.removeStyle( 'float' );
  878 + else
  879 + wrapper.setStyle( 'float', align );
  880 +
  881 + wrapper.removeStyle( 'text-align' );
  882 + }
  883 + }
  884 + }
  885 +
  886 + // Returns a function that creates widgets from all <img> and
  887 + // <figure class="{config.image2_captionedClass}"> elements.
  888 + //
  889 + // @param {CKEDITOR.editor} editor
  890 + // @returns {Function}
  891 + function upcastWidgetElement( editor ) {
  892 + var isCenterWrapper = centerWrapperChecker( editor ),
  893 + captionedClass = editor.config.image2_captionedClass;
  894 +
  895 + // @param {CKEDITOR.htmlParser.element} el
  896 + // @param {Object} data
  897 + return function( el, data ) {
  898 + var dimensions = { width: 1, height: 1 },
  899 + name = el.name,
  900 + image;
  901 +
  902 + // http://dev.ckeditor.com/ticket/11110 Don't initialize on pasted fake objects.
  903 + if ( el.attributes[ 'data-cke-realelement' ] )
  904 + return;
  905 +
  906 + // If a center wrapper is found, there are 3 possible cases:
  907 + //
  908 + // 1. <div style="text-align:center"><figure>...</figure></div>.
  909 + // In this case centering is done with a class set on widget.wrapper.
  910 + // Simply replace centering wrapper with figure (it's no longer necessary).
  911 + //
  912 + // 2. <p style="text-align:center"><img/></p>.
  913 + // Nothing to do here: <p> remains for styling purposes.
  914 + //
  915 + // 3. <div style="text-align:center"><img/></div>.
  916 + // Nothing to do here (2.) but that case is only possible in enterMode different
  917 + // than ENTER_P.
  918 + if ( isCenterWrapper( el ) ) {
  919 + if ( name == 'div' ) {
  920 + var figure = el.getFirst( 'figure' );
  921 +
  922 + // Case #1.
  923 + if ( figure ) {
  924 + el.replaceWith( figure );
  925 + el = figure;
  926 + }
  927 + }
  928 + // Cases #2 and #3 (handled transparently)
  929 +
  930 + // If there's a centering wrapper, save it in data.
  931 + data.align = 'center';
  932 +
  933 + // Image can be wrapped in link <a><img/></a>.
  934 + image = el.getFirst( 'img' ) || el.getFirst( 'a' ).getFirst( 'img' );
  935 + }
  936 +
  937 + // No center wrapper has been found.
  938 + else if ( name == 'figure' && el.hasClass( captionedClass ) ) {
  939 + image = el.getFirst( 'img' ) || el.getFirst( 'a' ).getFirst( 'img' );
  940 +
  941 + // Upcast linked image like <a><img/></a>.
  942 + } else if ( isLinkedOrStandaloneImage( el ) ) {
  943 + image = el.name == 'a' ? el.children[ 0 ] : el;
  944 + }
  945 +
  946 + if ( !image )
  947 + return;
  948 +
  949 + // If there's an image, then cool, we got a widget.
  950 + // Now just remove dimension attributes expressed with %.
  951 + for ( var d in dimensions ) {
  952 + var dimension = image.attributes[ d ];
  953 +
  954 + if ( dimension && dimension.match( regexPercent ) )
  955 + delete image.attributes[ d ];
  956 + }
  957 +
  958 + return el;
  959 + };
  960 + }
  961 +
  962 + // Returns a function which transforms the widget to the external format
  963 + // according to the current configuration.
  964 + //
  965 + // @param {CKEDITOR.editor}
  966 + function downcastWidgetElement( editor ) {
  967 + var alignClasses = editor.config.image2_alignClasses;
  968 +
  969 + // @param {CKEDITOR.htmlParser.element} el
  970 + return function( el ) {
  971 + // In case of <a><img/></a>, <img/> is the element to hold
  972 + // inline styles or classes (image2_alignClasses).
  973 + var attrsHolder = el.name == 'a' ? el.getFirst() : el,
  974 + attrs = attrsHolder.attributes,
  975 + align = this.data.align;
  976 +
  977 + // De-wrap the image from resize handle wrapper.
  978 + // Only block widgets have one.
  979 + if ( !this.inline ) {
  980 + var resizeWrapper = el.getFirst( 'span' );
  981 +
  982 + if ( resizeWrapper )
  983 + resizeWrapper.replaceWith( resizeWrapper.getFirst( { img: 1, a: 1 } ) );
  984 + }
  985 +
  986 + if ( align && align != 'none' ) {
  987 + var styles = CKEDITOR.tools.parseCssText( attrs.style || '' );
  988 +
  989 + // When the widget is captioned (<figure>) and internally centering is done
  990 + // with widget's wrapper style/class, in the external data representation,
  991 + // <figure> must be wrapped with an element holding an style/class:
  992 + //
  993 + // <div style="text-align:center">
  994 + // <figure class="image" style="display:inline-block">...</figure>
  995 + // </div>
  996 + // or
  997 + // <div class="some-center-class">
  998 + // <figure class="image">...</figure>
  999 + // </div>
  1000 + //
  1001 + if ( align == 'center' && el.name == 'figure' ) {
  1002 + el = el.wrapWith( new CKEDITOR.htmlParser.element( 'div',
  1003 + alignClasses ? { 'class': alignClasses[ 1 ] } : { style: 'text-align:center' } ) );
  1004 + }
  1005 +
  1006 + // If left/right, add float style to the downcasted element.
  1007 + else if ( align in { left: 1, right: 1 } ) {
  1008 + if ( alignClasses )
  1009 + attrsHolder.addClass( alignClasses[ alignmentsObj[ align ] ] );
  1010 + else
  1011 + styles[ 'float' ] = align;
  1012 + }
  1013 +
  1014 + // Update element styles.
  1015 + if ( !alignClasses && !CKEDITOR.tools.isEmpty( styles ) )
  1016 + attrs.style = CKEDITOR.tools.writeCssText( styles );
  1017 + }
  1018 +
  1019 + return el;
  1020 + };
  1021 + }
  1022 +
  1023 + // Returns a function that checks if an element is a centering wrapper.
  1024 + //
  1025 + // @param {CKEDITOR.editor} editor
  1026 + // @returns {Function}
  1027 + function centerWrapperChecker( editor ) {
  1028 + var captionedClass = editor.config.image2_captionedClass,
  1029 + alignClasses = editor.config.image2_alignClasses,
  1030 + validChildren = { figure: 1, a: 1, img: 1 };
  1031 +
  1032 + return function( el ) {
  1033 + // Wrapper must be either <div> or <p>.
  1034 + if ( !( el.name in { div: 1, p: 1 } ) )
  1035 + return false;
  1036 +
  1037 + var children = el.children;
  1038 +
  1039 + // Centering wrapper can have only one child.
  1040 + if ( children.length !== 1 )
  1041 + return false;
  1042 +
  1043 + var child = children[ 0 ];
  1044 +
  1045 + // Only <figure> or <img /> can be first (only) child of centering wrapper,
  1046 + // regardless of its type.
  1047 + if ( !( child.name in validChildren ) )
  1048 + return false;
  1049 +
  1050 + // If centering wrapper is <p>, only <img /> can be the child.
  1051 + // <p style="text-align:center"><img /></p>
  1052 + if ( el.name == 'p' ) {
  1053 + if ( !isLinkedOrStandaloneImage( child ) )
  1054 + return false;
  1055 + }
  1056 + // Centering <div> can hold <img/> or <figure>, depending on enterMode.
  1057 + else {
  1058 + // If a <figure> is the first (only) child, it must have a class.
  1059 + // <div style="text-align:center"><figure>...</figure><div>
  1060 + if ( child.name == 'figure' ) {
  1061 + if ( !child.hasClass( captionedClass ) )
  1062 + return false;
  1063 + } else {
  1064 + // Centering <div> can hold <img/> or <a><img/></a> only when enterMode
  1065 + // is ENTER_(BR|DIV).
  1066 + // <div style="text-align:center"><img /></div>
  1067 + // <div style="text-align:center"><a><img /></a></div>
  1068 + if ( editor.enterMode == CKEDITOR.ENTER_P )
  1069 + return false;
  1070 +
  1071 + // Regardless of enterMode, a child which is not <figure> must be
  1072 + // either <img/> or <a><img/></a>.
  1073 + if ( !isLinkedOrStandaloneImage( child ) )
  1074 + return false;
  1075 + }
  1076 + }
  1077 +
  1078 + // Centering wrapper got to be... centering. If image2_alignClasses are defined,
  1079 + // check for centering class. Otherwise, check the style.
  1080 + if ( alignClasses ? el.hasClass( alignClasses[ 1 ] ) :
  1081 + CKEDITOR.tools.parseCssText( el.attributes.style || '', true )[ 'text-align' ] == 'center' )
  1082 + return true;
  1083 +
  1084 + return false;
  1085 + };
  1086 + }
  1087 +
  1088 + // Checks whether element is <img/> or <a><img/></a>.
  1089 + //
  1090 + // @param {CKEDITOR.htmlParser.element}
  1091 + function isLinkedOrStandaloneImage( el ) {
  1092 + if ( el.name == 'img' )
  1093 + return true;
  1094 + else if ( el.name == 'a' )
  1095 + return el.children.length == 1 && el.getFirst( 'img' );
  1096 +
  1097 + return false;
  1098 + }
  1099 +
  1100 + // Sets width and height of the widget image according to current widget data.
  1101 + //
  1102 + // @param {CKEDITOR.plugins.widget} widget
  1103 + function setDimensions( widget ) {
  1104 + var data = widget.data,
  1105 + dimensions = { width: data.width, height: data.height },
  1106 + image = widget.parts.image;
  1107 +
  1108 + for ( var d in dimensions ) {
  1109 + if ( dimensions[ d ] )
  1110 + image.setAttribute( d, dimensions[ d ] );
  1111 + else
  1112 + image.removeAttribute( d );
  1113 + }
  1114 + }
  1115 +
  1116 + // Defines all features related to drag-driven image resizing.
  1117 + //
  1118 + // @param {CKEDITOR.plugins.widget} widget
  1119 + function setupResizer( widget ) {
  1120 + var editor = widget.editor,
  1121 + editable = editor.editable(),
  1122 + doc = editor.document,
  1123 +
  1124 + // Store the resizer in a widget for testing (http://dev.ckeditor.com/ticket/11004).
  1125 + resizer = widget.resizer = doc.createElement( 'span' );
  1126 +
  1127 + resizer.addClass( 'cke_image_resizer' );
  1128 + resizer.setAttribute( 'title', editor.lang.image2.resizer );
  1129 + resizer.append( new CKEDITOR.dom.text( '\u200b', doc ) );
  1130 +
  1131 + // Inline widgets don't need a resizer wrapper as an image spans the entire widget.
  1132 + if ( !widget.inline ) {
  1133 + var imageOrLink = widget.parts.link || widget.parts.image,
  1134 + oldResizeWrapper = imageOrLink.getParent(),
  1135 + resizeWrapper = doc.createElement( 'span' );
  1136 +
  1137 + resizeWrapper.addClass( 'cke_image_resizer_wrapper' );
  1138 + resizeWrapper.append( imageOrLink );
  1139 + resizeWrapper.append( resizer );
  1140 + widget.element.append( resizeWrapper, true );
  1141 +
  1142 + // Remove the old wrapper which could came from e.g. pasted HTML
  1143 + // and which could be corrupted (e.g. resizer span has been lost).
  1144 + if ( oldResizeWrapper.is( 'span' ) )
  1145 + oldResizeWrapper.remove();
  1146 + } else {
  1147 + widget.wrapper.append( resizer );
  1148 + }
  1149 +
  1150 + // Calculate values of size variables and mouse offsets.
  1151 + resizer.on( 'mousedown', function( evt ) {
  1152 + var image = widget.parts.image,
  1153 +
  1154 + // "factor" can be either 1 or -1. I.e.: For right-aligned images, we need to
  1155 + // subtract the difference to get proper width, etc. Without "factor",
  1156 + // resizer starts working the opposite way.
  1157 + factor = widget.data.align == 'right' ? -1 : 1,
  1158 +
  1159 + // The x-coordinate of the mouse relative to the screen
  1160 + // when button gets pressed.
  1161 + startX = evt.data.$.screenX,
  1162 + startY = evt.data.$.screenY,
  1163 +
  1164 + // The initial dimensions and aspect ratio of the image.
  1165 + startWidth = image.$.clientWidth,
  1166 + startHeight = image.$.clientHeight,
  1167 + ratio = startWidth / startHeight,
  1168 +
  1169 + listeners = [],
  1170 +
  1171 + // A class applied to editable during resizing.
  1172 + cursorClass = 'cke_image_s' + ( !~factor ? 'w' : 'e' ),
  1173 +
  1174 + nativeEvt, newWidth, newHeight, updateData,
  1175 + moveDiffX, moveDiffY, moveRatio;
  1176 +
  1177 + // Save the undo snapshot first: before resizing.
  1178 + editor.fire( 'saveSnapshot' );
  1179 +
  1180 + // Mousemove listeners are removed on mouseup.
  1181 + attachToDocuments( 'mousemove', onMouseMove, listeners );
  1182 +
  1183 + // Clean up the mousemove listener. Update widget data if valid.
  1184 + attachToDocuments( 'mouseup', onMouseUp, listeners );
  1185 +
  1186 + // The entire editable will have the special cursor while resizing goes on.
  1187 + editable.addClass( cursorClass );
  1188 +
  1189 + // This is to always keep the resizer element visible while resizing.
  1190 + resizer.addClass( 'cke_image_resizing' );
  1191 +
  1192 + // Attaches an event to a global document if inline editor.
  1193 + // Additionally, if classic (`iframe`-based) editor, also attaches the same event to `iframe`'s document.
  1194 + function attachToDocuments( name, callback, collection ) {
  1195 + var globalDoc = CKEDITOR.document,
  1196 + listeners = [];
  1197 +
  1198 + if ( !doc.equals( globalDoc ) )
  1199 + listeners.push( globalDoc.on( name, callback ) );
  1200 +
  1201 + listeners.push( doc.on( name, callback ) );
  1202 +
  1203 + if ( collection ) {
  1204 + for ( var i = listeners.length; i--; )
  1205 + collection.push( listeners.pop() );
  1206 + }
  1207 + }
  1208 +
  1209 + // Calculate with first, and then adjust height, preserving ratio.
  1210 + function adjustToX() {
  1211 + newWidth = startWidth + factor * moveDiffX;
  1212 + newHeight = Math.round( newWidth / ratio );
  1213 + }
  1214 +
  1215 + // Calculate height first, and then adjust width, preserving ratio.
  1216 + function adjustToY() {
  1217 + newHeight = startHeight - moveDiffY;
  1218 + newWidth = Math.round( newHeight * ratio );
  1219 + }
  1220 +
  1221 + // This is how variables refer to the geometry.
  1222 + // Note: x corresponds to moveOffset, this is the position of mouse
  1223 + // Note: o corresponds to [startX, startY].
  1224 + //
  1225 + // +--------------+--------------+
  1226 + // | | |
  1227 + // | I | II |
  1228 + // | | |
  1229 + // +------------- o -------------+ _ _ _
  1230 + // | | | ^
  1231 + // | VI | III | | moveDiffY
  1232 + // | | x _ _ _ _ _ v
  1233 + // +--------------+---------|----+
  1234 + // | |
  1235 + // <------->
  1236 + // moveDiffX
  1237 + function onMouseMove( evt ) {
  1238 + nativeEvt = evt.data.$;
  1239 +
  1240 + // This is how far the mouse is from the point the button was pressed.
  1241 + moveDiffX = nativeEvt.screenX - startX;
  1242 + moveDiffY = startY - nativeEvt.screenY;
  1243 +
  1244 + // This is the aspect ratio of the move difference.
  1245 + moveRatio = Math.abs( moveDiffX / moveDiffY );
  1246 +
  1247 + // Left, center or none-aligned widget.
  1248 + if ( factor == 1 ) {
  1249 + if ( moveDiffX <= 0 ) {
  1250 + // Case: IV.
  1251 + if ( moveDiffY <= 0 )
  1252 + adjustToX();
  1253 +
  1254 + // Case: I.
  1255 + else {
  1256 + if ( moveRatio >= ratio )
  1257 + adjustToX();
  1258 + else
  1259 + adjustToY();
  1260 + }
  1261 + } else {
  1262 + // Case: III.
  1263 + if ( moveDiffY <= 0 ) {
  1264 + if ( moveRatio >= ratio )
  1265 + adjustToY();
  1266 + else
  1267 + adjustToX();
  1268 + }
  1269 +
  1270 + // Case: II.
  1271 + else {
  1272 + adjustToY();
  1273 + }
  1274 + }
  1275 + }
  1276 +
  1277 + // Right-aligned widget. It mirrors behaviours, so I becomes II,
  1278 + // IV becomes III and vice-versa.
  1279 + else {
  1280 + if ( moveDiffX <= 0 ) {
  1281 + // Case: IV.
  1282 + if ( moveDiffY <= 0 ) {
  1283 + if ( moveRatio >= ratio )
  1284 + adjustToY();
  1285 + else
  1286 + adjustToX();
  1287 + }
  1288 +
  1289 + // Case: I.
  1290 + else {
  1291 + adjustToY();
  1292 + }
  1293 + } else {
  1294 + // Case: III.
  1295 + if ( moveDiffY <= 0 )
  1296 + adjustToX();
  1297 +
  1298 + // Case: II.
  1299 + else {
  1300 + if ( moveRatio >= ratio ) {
  1301 + adjustToX();
  1302 + } else {
  1303 + adjustToY();
  1304 + }
  1305 + }
  1306 + }
  1307 + }
  1308 +
  1309 + // Don't update attributes if less than 10.
  1310 + // This is to prevent images to visually disappear.
  1311 + if ( newWidth >= 15 && newHeight >= 15 ) {
  1312 + image.setAttributes( { width: newWidth, height: newHeight } );
  1313 + updateData = true;
  1314 + } else {
  1315 + updateData = false;
  1316 + }
  1317 + }
  1318 +
  1319 + function onMouseUp() {
  1320 + var l;
  1321 +
  1322 + while ( ( l = listeners.pop() ) )
  1323 + l.removeListener();
  1324 +
  1325 + // Restore default cursor by removing special class.
  1326 + editable.removeClass( cursorClass );
  1327 +
  1328 + // This is to bring back the regular behaviour of the resizer.
  1329 + resizer.removeClass( 'cke_image_resizing' );
  1330 +
  1331 + if ( updateData ) {
  1332 + widget.setData( { width: newWidth, height: newHeight } );
  1333 +
  1334 + // Save another undo snapshot: after resizing.
  1335 + editor.fire( 'saveSnapshot' );
  1336 + }
  1337 +
  1338 + // Don't update data twice or more.
  1339 + updateData = false;
  1340 + }
  1341 + } );
  1342 +
  1343 + // Change the position of the widget resizer when data changes.
  1344 + widget.on( 'data', function() {
  1345 + resizer[ widget.data.align == 'right' ? 'addClass' : 'removeClass' ]( 'cke_image_resizer_left' );
  1346 + } );
  1347 + }
  1348 +
  1349 + // Integrates widget alignment setting with justify
  1350 + // plugin's commands (execution and refreshment).
  1351 + // @param {CKEDITOR.editor} editor
  1352 + // @param {String} value 'left', 'right', 'center' or 'block'
  1353 + function alignCommandIntegrator( editor ) {
  1354 + var execCallbacks = [],
  1355 + enabled;
  1356 +
  1357 + return function( value ) {
  1358 + var command = editor.getCommand( 'justify' + value );
  1359 +
  1360 + // Most likely, the justify plugin isn't loaded.
  1361 + if ( !command )
  1362 + return;
  1363 +
  1364 + // This command will be manually refreshed along with
  1365 + // other commands after exec.
  1366 + execCallbacks.push( function() {
  1367 + command.refresh( editor, editor.elementPath() );
  1368 + } );
  1369 +
  1370 + if ( value in { right: 1, left: 1, center: 1 } ) {
  1371 + command.on( 'exec', function( evt ) {
  1372 + var widget = getFocusedWidget( editor );
  1373 +
  1374 + if ( widget ) {
  1375 + widget.setData( 'align', value );
  1376 +
  1377 + // Once the widget changed its align, all the align commands
  1378 + // must be refreshed: the event is to be cancelled.
  1379 + for ( var i = execCallbacks.length; i--; )
  1380 + execCallbacks[ i ]();
  1381 +
  1382 + evt.cancel();
  1383 + }
  1384 + } );
  1385 + }
  1386 +
  1387 + command.on( 'refresh', function( evt ) {
  1388 + var widget = getFocusedWidget( editor ),
  1389 + allowed = { right: 1, left: 1, center: 1 };
  1390 +
  1391 + if ( !widget )
  1392 + return;
  1393 +
  1394 + // Cache "enabled" on first use. This is because filter#checkFeature may
  1395 + // not be available during plugin's afterInit in the future — a moment when
  1396 + // alignCommandIntegrator is called.
  1397 + if ( enabled === undefined )
  1398 + enabled = editor.filter.checkFeature( editor.widgets.registered.image.features.align );
  1399 +
  1400 + // Don't allow justify commands when widget alignment is disabled (http://dev.ckeditor.com/ticket/11004).
  1401 + if ( !enabled )
  1402 + this.setState( CKEDITOR.TRISTATE_DISABLED );
  1403 + else {
  1404 + this.setState(
  1405 + ( widget.data.align == value ) ? (
  1406 + CKEDITOR.TRISTATE_ON
  1407 + ) : (
  1408 + ( value in allowed ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED
  1409 + )
  1410 + );
  1411 + }
  1412 +
  1413 + evt.cancel();
  1414 + } );
  1415 + };
  1416 + }
  1417 +
  1418 + function linkCommandIntegrator( editor ) {
  1419 + // Nothing to integrate with if link is not loaded.
  1420 + if ( !editor.plugins.link )
  1421 + return;
  1422 +
  1423 + CKEDITOR.on( 'dialogDefinition', function( evt ) {
  1424 + var dialog = evt.data;
  1425 +
  1426 + if ( dialog.name == 'link' ) {
  1427 + var def = dialog.definition;
  1428 +
  1429 + var onShow = def.onShow,
  1430 + onOk = def.onOk;
  1431 +
  1432 + def.onShow = function() {
  1433 + var widget = getFocusedWidget( editor ),
  1434 + displayTextField = this.getContentElement( 'info', 'linkDisplayText' ).getElement().getParent().getParent();
  1435 +
  1436 + // Widget cannot be enclosed in a link, i.e.
  1437 + // <a>foo<inline widget/>bar</a>
  1438 + if ( widget && ( widget.inline ? !widget.wrapper.getAscendant( 'a' ) : 1 ) ) {
  1439 + this.setupContent( widget.data.link || {} );
  1440 +
  1441 + // Hide the display text in case of linking image2 widget.
  1442 + displayTextField.hide();
  1443 + } else {
  1444 + // Make sure that display text is visible, as it might be hidden by image2 integration
  1445 + // before.
  1446 + displayTextField.show();
  1447 + onShow.apply( this, arguments );
  1448 + }
  1449 + };
  1450 +
  1451 + // Set widget data if linking the widget using
  1452 + // link dialog (instead of default action).
  1453 + // State shifter handles data change and takes
  1454 + // care of internal DOM structure of linked widget.
  1455 + def.onOk = function() {
  1456 + var widget = getFocusedWidget( editor );
  1457 +
  1458 + // Widget cannot be enclosed in a link, i.e.
  1459 + // <a>foo<inline widget/>bar</a>
  1460 + if ( widget && ( widget.inline ? !widget.wrapper.getAscendant( 'a' ) : 1 ) ) {
  1461 + var data = {};
  1462 +
  1463 + // Collect data from fields.
  1464 + this.commitContent( data );
  1465 +
  1466 + // Set collected data to widget.
  1467 + widget.setData( 'link', data );
  1468 + } else {
  1469 + onOk.apply( this, arguments );
  1470 + }
  1471 + };
  1472 + }
  1473 + } );
  1474 +
  1475 + // Overwrite default behaviour of unlink command.
  1476 + editor.getCommand( 'unlink' ).on( 'exec', function( evt ) {
  1477 + var widget = getFocusedWidget( editor );
  1478 +
  1479 + // Override unlink only when link truly belongs to the widget.
  1480 + // If wrapped inline widget in a link, let default unlink work (http://dev.ckeditor.com/ticket/11814).
  1481 + if ( !widget || !widget.parts.link )
  1482 + return;
  1483 +
  1484 + widget.setData( 'link', null );
  1485 +
  1486 + // Selection (which is fake) may not change if unlinked image in focused widget,
  1487 + // i.e. if captioned image. Let's refresh command state manually here.
  1488 + this.refresh( editor, editor.elementPath() );
  1489 +
  1490 + evt.cancel();
  1491 + } );
  1492 +
  1493 + // Overwrite default refresh of unlink command.
  1494 + editor.getCommand( 'unlink' ).on( 'refresh', function( evt ) {
  1495 + var widget = getFocusedWidget( editor );
  1496 +
  1497 + if ( !widget )
  1498 + return;
  1499 +
  1500 + // Note that widget may be wrapped in a link, which
  1501 + // does not belong to that widget (http://dev.ckeditor.com/ticket/11814).
  1502 + this.setState( widget.data.link || widget.wrapper.getAscendant( 'a' ) ?
  1503 + CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED );
  1504 +
  1505 + evt.cancel();
  1506 + } );
  1507 + }
  1508 +
  1509 + // Returns the focused widget, if of the type specific for this plugin.
  1510 + // If no widget is focused, `null` is returned.
  1511 + //
  1512 + // @param {CKEDITOR.editor}
  1513 + // @returns {CKEDITOR.plugins.widget}
  1514 + function getFocusedWidget( editor ) {
  1515 + var widget = editor.widgets.focused;
  1516 +
  1517 + if ( widget && widget.name == 'image' )
  1518 + return widget;
  1519 +
  1520 + return null;
  1521 + }
  1522 +
  1523 + // Returns a set of widget allowedContent rules, depending
  1524 + // on configurations like config#image2_alignClasses or
  1525 + // config#image2_captionedClass.
  1526 + //
  1527 + // @param {CKEDITOR.editor}
  1528 + // @returns {Object}
  1529 + function getWidgetAllowedContent( editor ) {
  1530 + var alignClasses = editor.config.image2_alignClasses,
  1531 + rules = {
  1532 + // Widget may need <div> or <p> centering wrapper.
  1533 + div: {
  1534 + match: centerWrapperChecker( editor )
  1535 + },
  1536 + p: {
  1537 + match: centerWrapperChecker( editor )
  1538 + },
  1539 + img: {
  1540 + attributes: '!src,alt,width,height'
  1541 + },
  1542 + figure: {
  1543 + classes: '!' + editor.config.image2_captionedClass
  1544 + },
  1545 + figcaption: true
  1546 + };
  1547 +
  1548 + if ( alignClasses ) {
  1549 + // Centering class from the config.
  1550 + rules.div.classes = alignClasses[ 1 ];
  1551 + rules.p.classes = rules.div.classes;
  1552 +
  1553 + // Left/right classes from the config.
  1554 + rules.img.classes = alignClasses[ 0 ] + ',' + alignClasses[ 2 ];
  1555 + rules.figure.classes += ',' + rules.img.classes;
  1556 + } else {
  1557 + // Centering with text-align.
  1558 + rules.div.styles = 'text-align';
  1559 + rules.p.styles = 'text-align';
  1560 +
  1561 + rules.img.styles = 'float';
  1562 + rules.figure.styles = 'float,display';
  1563 + }
  1564 +
  1565 + return rules;
  1566 + }
  1567 +
  1568 + // Returns a set of widget feature rules, depending
  1569 + // on editor configuration. Note that the following may not cover
  1570 + // all the possible cases since requiredContent supports a single
  1571 + // tag only.
  1572 + //
  1573 + // @param {CKEDITOR.editor}
  1574 + // @returns {Object}
  1575 + function getWidgetFeatures( editor ) {
  1576 + var alignClasses = editor.config.image2_alignClasses,
  1577 + features = {
  1578 + dimension: {
  1579 + requiredContent: 'img[width,height]'
  1580 + },
  1581 + align: {
  1582 + requiredContent: 'img' +
  1583 + ( alignClasses ? '(' + alignClasses[ 0 ] + ')' : '{float}' )
  1584 + },
  1585 + caption: {
  1586 + requiredContent: 'figcaption'
  1587 + }
  1588 + };
  1589 +
  1590 + return features;
  1591 + }
  1592 +
  1593 + // Returns element which is styled, considering current
  1594 + // state of the widget.
  1595 + //
  1596 + // @see CKEDITOR.plugins.widget#applyStyle
  1597 + // @param {CKEDITOR.plugins.widget} widget
  1598 + // @returns {CKEDITOR.dom.element}
  1599 + function getStyleableElement( widget ) {
  1600 + return widget.data.hasCaption ? widget.element : widget.parts.image;
  1601 + }
  1602 +} )();
  1603 +
  1604 +/**
  1605 + * A CSS class applied to the `<figure>` element of a captioned image.
  1606 + *
  1607 + * Read more in the [documentation](#!/guide/dev_captionedimage) and see the
  1608 + * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html).
  1609 + *
  1610 + * // Changes the class to "captionedImage".
  1611 + * config.image2_captionedClass = 'captionedImage';
  1612 + *
  1613 + * @cfg {String} [image2_captionedClass='image']
  1614 + * @member CKEDITOR.config
  1615 + */
  1616 +CKEDITOR.config.image2_captionedClass = 'image';
  1617 +
  1618 +/**
  1619 + * Determines whether dimension inputs should be automatically filled when the image URL changes in the Enhanced Image
  1620 + * plugin dialog window.
  1621 + *
  1622 + * Read more in the [documentation](#!/guide/dev_captionedimage) and see the
  1623 + * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html).
  1624 + *
  1625 + * config.image2_prefillDimensions = false;
  1626 + *
  1627 + * @since 4.5
  1628 + * @cfg {Boolean} [image2_prefillDimensions=true]
  1629 + * @member CKEDITOR.config
  1630 + */
  1631 +
  1632 +/**
  1633 + * Disables the image resizer. By default the resizer is enabled.
  1634 + *
  1635 + * Read more in the [documentation](#!/guide/dev_captionedimage) and see the
  1636 + * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html).
  1637 + *
  1638 + * config.image2_disableResizer = true;
  1639 + *
  1640 + * @since 4.5
  1641 + * @cfg {Boolean} [image2_disableResizer=false]
  1642 + * @member CKEDITOR.config
  1643 + */
  1644 +
  1645 +/**
  1646 + * CSS classes applied to aligned images. Useful to take control over the way
  1647 + * the images are aligned, i.e. to customize output HTML and integrate external stylesheets.
  1648 + *
  1649 + * Classes should be defined in an array of three elements, containing left, center, and right
  1650 + * alignment classes, respectively. For example:
  1651 + *
  1652 + * config.image2_alignClasses = [ 'align-left', 'align-center', 'align-right' ];
  1653 + *
  1654 + * **Note**: Once this configuration option is set, the plugin will no longer produce inline
  1655 + * styles for alignment. It means that e.g. the following HTML will be produced:
  1656 + *
  1657 + * <img alt="My image" class="custom-center-class" src="foo.png" />
  1658 + *
  1659 + * instead of:
  1660 + *
  1661 + * <img alt="My image" style="float:left" src="foo.png" />
  1662 + *
  1663 + * **Note**: Once this configuration option is set, corresponding style definitions
  1664 + * must be supplied to the editor:
  1665 + *
  1666 + * * For [classic editor](#!/guide/dev_framed) it can be done by defining additional
  1667 + * styles in the {@link CKEDITOR.config#contentsCss stylesheets loaded by the editor}. The same
  1668 + * styles must be provided on the target page where the content will be loaded.
  1669 + * * For [inline editor](#!/guide/dev_inline) the styles can be defined directly
  1670 + * with `<style> ... <style>` or `<link href="..." rel="stylesheet">`, i.e. within the `<head>`
  1671 + * of the page.
  1672 + *
  1673 + * For example, considering the following configuration:
  1674 + *
  1675 + * config.image2_alignClasses = [ 'align-left', 'align-center', 'align-right' ];
  1676 + *
  1677 + * CSS rules can be defined as follows:
  1678 + *
  1679 + * .align-left {
  1680 + * float: left;
  1681 + * }
  1682 + *
  1683 + * .align-right {
  1684 + * float: right;
  1685 + * }
  1686 + *
  1687 + * .align-center {
  1688 + * text-align: center;
  1689 + * }
  1690 + *
  1691 + * .align-center > figure {
  1692 + * display: inline-block;
  1693 + * }
  1694 + *
  1695 + * Read more in the [documentation](#!/guide/dev_captionedimage) and see the
  1696 + * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html).
  1697 + *
  1698 + * @since 4.4
  1699 + * @cfg {String[]} [image2_alignClasses=null]
  1700 + * @member CKEDITOR.config
  1701 + */
  1702 +
  1703 +/**
  1704 + * Determines whether alternative text is required for the captioned image.
  1705 + *
  1706 + * config.image2_altRequired = true;
  1707 + *
  1708 + * Read more in the [documentation](#!/guide/dev_captionedimage) and see the
  1709 + * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html).
  1710 + *
  1711 + * @since 4.6.0
  1712 + * @cfg {Boolean} [image2_altRequired=false]
  1713 + * @member CKEDITOR.config
  1714 + */
  1 +<!DOCTYPE html>
  2 +<!--
  3 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  4 +For licensing, see LICENSE.md or http://ckeditor.com/license
  5 +-->
  6 +<html>
  7 +<head>
  8 + <meta charset="utf-8">
  9 + <title>New Image plugin &mdash; CKEditor Sample</title>
  10 + <script src="../../../ckeditor.js"></script>
  11 + <script>
  12 + if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
  13 + CKEDITOR.tools.enableHtml5Elements( document );
  14 + </script>
  15 + <link href="../../../samples/old/sample.css" rel="stylesheet">
  16 + <meta name="ckeditor-sample-name" content="New Image plugin">
  17 + <meta name="ckeditor-sample-group" content="Plugins">
  18 + <meta name="ckeditor-sample-description" content="Using the new Image plugin to insert captioned images and adjust their dimensions.">
  19 + <meta name="ckeditor-sample-isnew" content="1">
  20 +</head>
  21 +<body>
  22 + <h1 class="samples">
  23 + <a href="../../../samples/old/index.html">CKEditor Samples</a> &raquo; New Image plugin
  24 + </h1>
  25 + <div class="warning deprecated">
  26 + This sample is not maintained anymore. Check out its <a href="http://sdk.ckeditor.com/samples/captionedimage.html">brand new version in CKEditor SDK</a>.
  27 + </div>
  28 +
  29 + <div class="description">
  30 + <p>
  31 + This editor is using the new <strong>Image</strong> (<code>image2</code>) plugin, which implements a dynamic <em>click-and-drag</em> resizing
  32 + and easy captioning of the images.
  33 + </p>
  34 + <p>
  35 + To use the new plugin, extend <code><a class="samples" href="http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-extraPlugins">config.extraPlugins</a></code>:
  36 + </p>
  37 +<pre class="samples">
  38 +CKEDITOR.replace( '<em>textarea_id</em>', {
  39 + <strong>extraPlugins: 'image2'</strong>
  40 +} );
  41 +</pre>
  42 + </div>
  43 +
  44 + <textarea id="editor1" cols="10" rows="10">
  45 + &lt;h1&gt;Apollo 11&lt;/h1&gt;&lt;figure class=&quot;image&quot; style=&quot;float: right&quot;&gt;&lt;img alt=&quot;Saturn V&quot; src=&quot;assets/image1.jpg&quot; width=&quot;200&quot; /&gt;&lt;figcaption&gt;Roll out of Saturn V on launch pad&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Apollo 11&lt;/strong&gt; was the spaceflight that landed the first humans, Americans &lt;a href=&quot;http://en.wikipedia.org/wiki/Neil_Armstrong&quot; title=&quot;Neil Armstrong&quot;&gt;Neil Armstrong&lt;/a&gt; and &lt;a href=&quot;http://en.wikipedia.org/wiki/Buzz_Aldrin&quot; title=&quot;Buzz Aldrin&quot;&gt;Buzz Aldrin&lt;/a&gt;, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.&lt;/p&gt;&lt;p&gt;Armstrong spent about &lt;s&gt;three and a half&lt;/s&gt; two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&amp;nbsp;kg) of lunar material for return to Earth. A third member of the mission, &lt;a href=&quot;http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)&quot; title=&quot;Michael Collins (astronaut)&quot;&gt;Michael Collins&lt;/a&gt;, piloted the &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_Command/Service_Module&quot; title=&quot;Apollo Command/Service Module&quot;&gt;command&lt;/a&gt; spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.&lt;/p&gt;&lt;h2&gt;Broadcasting and &lt;em&gt;quotes&lt;/em&gt; &lt;a id=&quot;quotes&quot; name=&quot;quotes&quot;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;One small step for [a] man, one giant leap for mankind.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;Apollo 11 effectively ended the &lt;a href=&quot;http://en.wikipedia.org/wiki/Space_Race&quot; title=&quot;Space Race&quot;&gt;Space Race&lt;/a&gt; and fulfilled a national goal proposed in 1961 by the late U.S. President &lt;a href=&quot;http://en.wikipedia.org/wiki/John_F._Kennedy&quot; title=&quot;John F. Kennedy&quot;&gt;John F. Kennedy&lt;/a&gt; in a speech before the United States Congress:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.&lt;/p&gt;&lt;/blockquote&gt;&lt;figure class=&quot;image&quot; style=&quot;float: right&quot;&gt;&lt;img alt=&quot;The Eagle&quot; src=&quot;assets/image2.jpg&quot; style=&quot;width: 200px&quot; /&gt;&lt;figcaption&gt;The Eagle in lunar orbit&lt;/figcaption&gt;&lt;/figure&gt;&lt;h2&gt;Technical details &lt;a id=&quot;tech-details&quot; name=&quot;tech-details&quot;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Launched by a &lt;strong&gt;Saturn V&lt;/strong&gt; rocket from &lt;a href=&quot;http://en.wikipedia.org/wiki/Kennedy_Space_Center&quot; title=&quot;Kennedy Space Center&quot;&gt;Kennedy Space Center&lt;/a&gt; in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of &lt;a href=&quot;http://en.wikipedia.org/wiki/NASA&quot; title=&quot;NASA&quot;&gt;NASA&lt;/a&gt;&amp;#39;s Apollo program. The Apollo spacecraft had three parts:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Command Module&lt;/strong&gt; with a cabin for the three astronauts which was the only part which landed back on Earth&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Service Module&lt;/strong&gt; which supported the Command Module with propulsion, electrical power, oxygen and water&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Lunar Module&lt;/strong&gt; for landing on the Moon.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;After being sent to the Moon by the Saturn V&amp;#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Mare_Tranquillitatis&quot; title=&quot;Mare Tranquillitatis&quot;&gt;Sea of Tranquility&lt;/a&gt;. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the &lt;a href=&quot;http://en.wikipedia.org/wiki/Pacific_Ocean&quot; title=&quot;Pacific Ocean&quot;&gt;Pacific Ocean&lt;/a&gt; on July 24.&lt;/p&gt;&lt;hr /&gt;&lt;p style=&quot;text-align:right&quot;&gt;&lt;small&gt;Source: &lt;a href=&quot;http://en.wikipedia.org/wiki/Apollo_11&quot;&gt;Wikipedia.org&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;
  46 + </textarea>
  47 +
  48 + <script>
  49 +
  50 + CKEDITOR.replace( 'editor1', {
  51 + extraPlugins: 'image2',
  52 + height: 450
  53 + } );
  54 +
  55 + </script>
  56 +
  57 + <div id="footer">
  58 + <hr>
  59 + <p>
  60 + CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
  61 + </p>
  62 + <p id="copy">
  63 + Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
  64 + Knabben. All rights reserved.
  65 + </p>
  66 + </div>
  67 +</body>
  68 +</html>
  1 +<!DOCTYPE html>
  2 +<!--
  3 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  4 +For licensing, see LICENSE.md or http://ckeditor.com/license
  5 +-->
  6 +<html>
  7 +<head>
  8 + <meta charset="utf-8">
  9 + <title>Widget Drag &amp; Drop with Lineutils &mdash; CKEditor Sample</title>
  10 + <script src="../../../ckeditor.js"></script>
  11 + <script>
  12 + if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 )
  13 + CKEDITOR.tools.enableHtml5Elements( document );
  14 + </script>
  15 + <link href="../../../samples/old/sample.css" rel="stylesheet">
  16 + <link href="../../image2/samples/contents.css" rel="stylesheet">
  17 +</head>
  18 +<body>
  19 + <h1 class="samples">
  20 + <a href="../../../samples/old/index.html">CKEditor Samples</a> &raquo; Widget Drag &amp; Drop with Lineutils
  21 + </h1>
  22 +
  23 + <h3>Classic (iframe-based) Editor</h3>
  24 +
  25 + <textarea id="editor1" cols="10" rows="10">
  26 + <h1>Apollo 11</h1>
  27 +
  28 + <figure class="caption" style="float:right"><img alt="Saturn V" src="../../image2/samples/assets/image1.jpg" width="200" />
  29 + <figcaption>Roll out of Saturn V on launch pad</figcaption>
  30 + </figure>
  31 +
  32 + <p><strong>Apollo 11</strong> was the spaceflight that landed the first humans, Americans <a href="http://en.wikipedia.org/wiki/Neil_Armstrong" title="Neil Armstrong">Neil Armstrong</a> and <a href="http://en.wikipedia.org/wiki/Buzz_Aldrin" title="Buzz Aldrin">Buzz Aldrin</a>, on the Moon on July 20, 1969, at 20:18 UTC. Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.</p>
  33 +
  34 + <p>Armstrong spent about <s>three and a half</s> two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5&nbsp;kg) of lunar material for return to Earth. A third member of the mission, <a href="http://en.wikipedia.org/wiki/Michael_Collins_(astronaut)" title="Michael Collins (astronaut)">Michael Collins</a>, piloted the <a href="http://en.wikipedia.org/wiki/Apollo_Command/Service_Module" title="Apollo Command/Service Module">command</a> spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.</p>
  35 +
  36 + <h2>Broadcasting and <em>quotes</em> <a id="quotes" name="quotes"></a></h2>
  37 +
  38 + <p>Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:</p>
  39 +
  40 + <blockquote>
  41 + <p>One small step for [a] man, one giant leap for mankind.</p>
  42 + </blockquote>
  43 +
  44 + <p>Apollo 11 effectively ended the <a href="http://en.wikipedia.org/wiki/Space_Race" title="Space Race">Space Race</a> and fulfilled a national goal proposed in 1961 by the late U.S. President <a href="http://en.wikipedia.org/wiki/John_F._Kennedy" title="John F. Kennedy">John F. Kennedy</a> in a speech before the United States Congress:</p>
  45 +
  46 + <div style="text-align:center">
  47 + <figure class="caption" style="display:inline-block"><img alt="The Eagle" height="123" src="../../image2/samples/assets/image2.jpg" width="136" />
  48 + <figcaption>The Eagle in lunar orbit</figcaption>
  49 + </figure>
  50 + </div>
  51 +
  52 + <blockquote>
  53 + <p>[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.</p>
  54 + </blockquote>
  55 +
  56 + <figure class="caption" style="float:right"><img alt="The Eagle" src="../../image2/samples/assets/image2.jpg" width="200" />
  57 + <figcaption>The Eagle in lunar orbit</figcaption>
  58 + </figure>
  59 +
  60 + <h2>Technical details <a id="tech-details" name="tech-details"></a></h2>
  61 +
  62 + <p>Launched by a <strong>Saturn V</strong> rocket from <a href="http://en.wikipedia.org/wiki/Kennedy_Space_Center" title="Kennedy Space Center">Kennedy Space Center</a> in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of <a href="http://en.wikipedia.org/wiki/NASA" title="NASA">NASA</a>&#39;s Apollo program. The Apollo spacecraft had three parts:</p>
  63 +
  64 + <ol>
  65 + <li><strong>Command Module</strong> with a cabin for the three astronauts which was the only part which landed back on Earth</li>
  66 + <li><strong>Service Module</strong> which supported the Command Module with propulsion, electrical power, oxygen and water</li>
  67 + <li><strong>Lunar Module</strong> for landing on the Moon.</li>
  68 + </ol>
  69 +
  70 + <p>After being sent to the Moon by the Saturn V&#39;s upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the <a href="http://en.wikipedia.org/wiki/Mare_Tranquillitatis" title="Mare Tranquillitatis">Sea of Tranquility</a>. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the <a href="http://en.wikipedia.org/wiki/Pacific_Ocean" title="Pacific Ocean">Pacific Ocean</a> on July 24.</p>
  71 +
  72 + <figure class="caption"><img alt="Saturn V" height="129" src="../../image2/samples/assets/image1.jpg" width="101" />
  73 + <figcaption>Roll out of Saturn V on launch pad</figcaption>
  74 + </figure>
  75 +
  76 + <hr />
  77 + <p style="text-align:right"><small>Source: <a href="http://en.wikipedia.org/wiki/Apollo_11">Wikipedia.org</a></small></p>
  78 +
  79 + </textarea>
  80 +
  81 + <h3>Inline Editor</h3>
  82 +
  83 + <div id="editor2" contenteditable="true" style="outline: 2px solid #ccc">
  84 + <table border="0" cellpadding="1" cellspacing="1" style="width: 100%; ">
  85 + <tbody>
  86 + <tr>
  87 + <td>This table</td>
  88 + <td>is the</td>
  89 + <td>very first</td>
  90 + <td>element of the document.</td>
  91 + </tr>
  92 + <tr>
  93 + <td>We are still</td>
  94 + <td>able to acces</td>
  95 + <td>the space before it.</td>
  96 + <td style="padding: 25px">
  97 + <table border="0" cellpadding="1" cellspacing="1" style="width: 100%; ">
  98 + <tbody>
  99 + <tr>
  100 + <td>This table is inside of a cell of another table.</td>
  101 + </tr>
  102 + <tr>
  103 + <td>We can type&nbsp;either before or after it though.</td>
  104 + </tr>
  105 + </tbody>
  106 + </table>
  107 + </td>
  108 + </tr>
  109 + </tbody>
  110 + </table>
  111 +
  112 + <hr />
  113 + <hr />
  114 + <ol style="width: 300px">
  115 + <li>This numbered list...</li>
  116 + <li>...is a neighbour of a horizontal line...</li>
  117 + <li style="padding: 20px;">
  118 + <ol>
  119 + <li>Nested list!</li>
  120 + </ol>
  121 + </li>
  122 + </ol>
  123 +
  124 + <figure class="caption"><img alt="Saturn V" src="../../image2/samples/assets/image1.jpg" width="100" />
  125 + <figcaption>Roll out of Saturn V on launch pad</figcaption>
  126 + </figure>
  127 +
  128 + <ul style="width: 450px">
  129 + <li>We can type between the lists...</li>
  130 + <li>...thanks to <strong>Magicline</strong>.</li>
  131 + </ul>
  132 +
  133 + <p>Lorem ipsum dolor sit amet dui. Morbi vel turpis. Nullam et leo. Etiam rutrum, urna tellus dui vel tincidunt mattis egestas, justo fringilla vel, massa. Phasellus.</p>
  134 +
  135 + <p>Quisque iaculis, dui lectus varius vitae, tortor. Proin lacus. Pellentesque ac lacus. Aenean nonummy commodo nec, pede. Etiam blandit risus elit.</p>
  136 +
  137 + <p>Ut pretium. Vestibulum rutrum in, adipiscing elit. Sed in quam in purus sem vitae pede. Pellentesque bibendum, urna sem vel risus. Vivamus posuere metus. Aliquam gravida iaculis nisl. Nam enim. Aliquam erat ac lacus tellus ac felis.</p>
  138 +
  139 + <div id="last" style="padding: 10px; text-align: center;">
  140 + <p>This text is wrapped in a&nbsp;<tt>DIV</tt>&nbsp;element. We can type after this element though.</p>
  141 + </div>
  142 + </div>
  143 +
  144 + <script>
  145 +
  146 + CKEDITOR.replace( 'editor1', {
  147 + extraPlugins: 'image2',
  148 + height: 450,
  149 + removePlugins: 'image,forms',
  150 + contentsCss: [ '../../../contents.css', '../../image2/samples/contents.css' ]
  151 + } );
  152 +
  153 + CKEDITOR.inline( 'editor2', {
  154 + extraPlugins: 'image2',
  155 + height: 450,
  156 + removePlugins: 'image,forms'
  157 + } );
  158 +
  159 + </script>
  160 +
  161 + <div id="footer">
  162 + <hr>
  163 + <p>
  164 + CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
  165 + </p>
  166 + <p id="copy">
  167 + Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
  168 + Knabben. All rights reserved.
  169 + </p>
  170 + </div>
  171 +</body>
  172 +</html>
  1 +<!DOCTYPE html>
  2 +<!--
  3 +Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  4 +For licensing, see LICENSE.md or http://ckeditor.com/license
  5 +-->
  6 +<html>
  7 +<head>
  8 + <meta charset="utf-8">
  9 + <title>Lineutils &mdash; CKEditor Sample</title>
  10 + <script src="../../../ckeditor.js"></script>
  11 + <link href="../../../samples/old/sample.css" rel="stylesheet">
  12 +</head>
  13 +<body>
  14 + <h1 class="samples">
  15 + <a href="../../../samples/old/index.html">CKEditor Samples</a> &raquo; Lineutils
  16 + </h1>
  17 +
  18 + <h3>Classic (iframe-based) Editor</h3>
  19 +
  20 + <textarea id="editor1" cols="10" rows="10">
  21 + <table border="0" cellpadding="1" cellspacing="1" style="width: 100%; ">
  22 + <tbody>
  23 + <tr>
  24 + <td>This table</td>
  25 + <td>is the</td>
  26 + <td>very first</td>
  27 + <td>element of the document.</td>
  28 + </tr>
  29 + <tr>
  30 + <td>We are still</td>
  31 + <td>able to acces</td>
  32 + <td>the space before it.</td>
  33 + <td style="padding: 25px">
  34 + <table border="0" cellpadding="1" cellspacing="1" style="width: 100%; ">
  35 + <tbody>
  36 + <tr>
  37 + <td>This table is inside of a cell of another table.</td>
  38 + </tr>
  39 + <tr>
  40 + <td>We can type&nbsp;either before or after it though.</td>
  41 + </tr>
  42 + </tbody>
  43 + </table>
  44 + </td>
  45 + </tr>
  46 + </tbody>
  47 + </table>
  48 +
  49 + <p>Two succesive horizontal lines (<tt>HR</tt> tags). We can access the space in between:</p>
  50 +
  51 + <hr />
  52 + <hr />
  53 + <ol style="width: 300px">
  54 + <li>This numbered list...</li>
  55 + <li>...is a neighbour of a horizontal line...</li>
  56 + <li style="padding: 20px;">
  57 + <ol>
  58 + <li>Nested list!</li>
  59 + </ol>
  60 + </li>
  61 + </ol>
  62 +
  63 + <ul style="width: 450px">
  64 + <li>We can type between the lists...</li>
  65 + <li>...thanks to <strong>Magicline</strong>.</li>
  66 + </ul>
  67 +
  68 + <p>Lorem ipsum dolor sit amet dui. Morbi vel turpis. Nullam et leo. Etiam rutrum, urna tellus dui vel tincidunt mattis egestas, justo fringilla vel, massa. Phasellus.</p>
  69 +
  70 + <p>Quisque iaculis, dui lectus varius vitae, tortor. Proin lacus. Pellentesque ac lacus. Aenean nonummy commodo nec, pede. Etiam blandit risus elit.</p>
  71 +
  72 + <p>Ut pretium. Vestibulum rutrum in, adipiscing elit. Sed in quam in purus sem vitae pede. Pellentesque bibendum, urna sem vel risus. Vivamus posuere metus. Aliquam gravida iaculis nisl. Nam enim. Aliquam erat ac lacus tellus ac felis.</p>
  73 +
  74 + <div id="last" style="padding: 10px; text-align: center;">
  75 + <p>This text is wrapped in a&nbsp;<tt>DIV</tt>&nbsp;element. We can type after this element though.</p>
  76 + </div>
  77 + </textarea>
  78 +
  79 + <h3>Inline Editor</h3>
  80 +
  81 + <div id="editor2" contenteditable="true" style="outline: 2px solid #ccc">
  82 + <table border="0" cellpadding="1" cellspacing="1" style="width: 100%; ">
  83 + <tbody>
  84 + <tr>
  85 + <td>This table</td>
  86 + <td>is the</td>
  87 + <td>very first</td>
  88 + <td>element of the document.</td>
  89 + </tr>
  90 + <tr>
  91 + <td>We are still</td>
  92 + <td>able to acces</td>
  93 + <td>the space before it.</td>
  94 + <td style="padding: 25px">
  95 + <table border="0" cellpadding="1" cellspacing="1" style="width: 100%; ">
  96 + <tbody>
  97 + <tr>
  98 + <td>This table is inside of a cell of another table.</td>
  99 + </tr>
  100 + <tr>
  101 + <td>We can type&nbsp;either before or after it though.</td>
  102 + </tr>
  103 + </tbody>
  104 + </table>
  105 + </td>
  106 + </tr>
  107 + </tbody>
  108 + </table>
  109 +
  110 + <p>Two succesive horizontal lines (<tt>HR</tt> tags). We can access the space in between:</p>
  111 +
  112 + <hr />
  113 + <hr />
  114 + <ol style="width: 300px">
  115 + <li>This numbered list...</li>
  116 + <li>...is a neighbour of a horizontal line...</li>
  117 + <li style="padding: 20px;">
  118 + <ol>
  119 + <li>Nested list!</li>
  120 + </ol>
  121 + </li>
  122 + </ol>
  123 +
  124 + <ul style="width: 450px">
  125 + <li>We can type between the lists...</li>
  126 + <li>...thanks to <strong>Magicline</strong>.</li>
  127 + </ul>
  128 +
  129 + <p>Lorem ipsum dolor sit amet dui. Morbi vel turpis. Nullam et leo. Etiam rutrum, urna tellus dui vel tincidunt mattis egestas, justo fringilla vel, massa. Phasellus.</p>
  130 +
  131 + <p>Quisque iaculis, dui lectus varius vitae, tortor. Proin lacus. Pellentesque ac lacus. Aenean nonummy commodo nec, pede. Etiam blandit risus elit.</p>
  132 +
  133 + <p>Ut pretium. Vestibulum rutrum in, adipiscing elit. Sed in quam in purus sem vitae pede. Pellentesque bibendum, urna sem vel risus. Vivamus posuere metus. Aliquam gravida iaculis nisl. Nam enim. Aliquam erat ac lacus tellus ac felis.</p>
  134 +
  135 + <div id="last" style="padding: 10px; text-align: center;">
  136 + <p>This text is wrapped in a&nbsp;<tt>DIV</tt>&nbsp;element. We can type after this element though.</p>
  137 + </div>
  138 + </div>
  139 +
  140 + <h3>Extreme inline</h3>
  141 +
  142 + <div id="editor3" contenteditable="true" style="left: 123px; outline: 1px solid red; border: 15px solid green; position: relative; top: 30; left: 30px;">
  143 + <div style="padding: 20px; background: gray; width: 300px" class="1">Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies. Curabitur et ligula. Ut molestie a, ultricies porta urna. Vestibulum commodo volutpat a, convallis ac, laoreet enim.</div>
  144 + <div style="background: violet; padding: 30px;" class="static">
  145 + Position static
  146 + <div style="background: green; padding: 30px; border: 14px solid orange">foo</div>
  147 + </div>
  148 + <dl class="2">
  149 + <dt>Key</dt><dd>Value</dd>
  150 + </dl>
  151 + <div>Whatever</div>
  152 + <hr id="hr">
  153 + <p>Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies</p>
  154 + <hr>
  155 + <hr>
  156 + <p>Lorem ipsum dolor sit amet enim. Etiam ullamcorper. Suspendisse a pellentesque dui, non felis. Maecenas malesuada elit lectus felis, malesuada ultricies</p>
  157 + <div style="background: green; padding: 30px; width: 200px">foo</div>
  158 + </div>
  159 +
  160 + <h3>Classic (iframe-based) Editor, H-scroll</h3>
  161 +
  162 + <textarea id="editor4" cols="10" rows="10">
  163 + <hr />
  164 + <hr />
  165 + <ol style="width: 1500px">
  166 + <li>This numbered list...</li>
  167 + <li>...is a neighbour of a horizontal line...</li>
  168 + <li style="padding: 20px;">
  169 + <ol>
  170 + <li>Nested list!</li>
  171 + </ol>
  172 + </li>
  173 + </ol>
  174 +
  175 + <ul style="width: 450px">
  176 + <li>We can type between the lists...</li>
  177 + <li>...thanks to <strong>Magicline</strong>.</li>
  178 + </ul>
  179 +
  180 + <p>Lorem ipsum dolor sit amet dui. Morbi vel turpis. Nullam et leo. Etiam rutrum, urna tellus dui vel tincidunt mattis egestas, justo fringilla vel, massa. Phasellus.</p>
  181 +
  182 + <p>Quisque iaculis, dui lectus varius vitae, tortor. Proin lacus. Pellentesque ac lacus. Aenean nonummy commodo nec, pede. Etiam blandit risus elit.</p>
  183 +
  184 + <p>Ut pretium. Vestibulum rutrum in, adipiscing elit. Sed in quam in purus sem vitae pede. Pellentesque bibendum, urna sem vel risus. Vivamus posuere metus. Aliquam gravida iaculis nisl. Nam enim. Aliquam erat ac lacus tellus ac felis.</p>
  185 +
  186 + <div id="last" style="padding: 10px; text-align: center;">
  187 + <p>This text is wrapped in a&nbsp;<tt>DIV</tt>&nbsp;element. We can type after this element though.</p>
  188 + </div>
  189 + </textarea>
  190 +
  191 + <script>
  192 +
  193 + CKEDITOR.addCss(
  194 + '.cke_editable * { outline: 1px solid #BCEBFF }'
  195 + );
  196 +
  197 + function callback() {
  198 + var helpers = CKEDITOR.plugins.lineutils;
  199 + var liner = new helpers.liner( this );
  200 + var locator = new helpers.locator( this );
  201 + var finder = new helpers.finder( this, {
  202 + lookups: {
  203 + 'is block and first child': function( el ) {
  204 + if ( el.is( CKEDITOR.dtd.$listItem ) )
  205 + return;
  206 +
  207 + if ( el.is( CKEDITOR.dtd.$block ) )
  208 + return CKEDITOR.LINEUTILS_BEFORE | CKEDITOR.LINEUTILS_AFTER;
  209 + }
  210 + }
  211 + } ).start( function( relations, x, y ) {
  212 + locator.locate( relations );
  213 +
  214 + var locations = locator.locations,
  215 + uid, type;
  216 +
  217 + liner.prepare( relations, locations );
  218 +
  219 + for ( uid in locations ) {
  220 + for ( type in locations[ uid ] )
  221 + liner.placeLine( { uid: uid, type: type } );
  222 + }
  223 +
  224 + liner.cleanup();
  225 + } );
  226 + }
  227 +
  228 + CKEDITOR.disableAutoInline = true;
  229 +
  230 + CKEDITOR.replace( 'editor1', {
  231 + extraPlugins: 'lineutils',
  232 + height: 450,
  233 + removePlugins: 'magicline',
  234 + allowedContent: true,
  235 + contentsCss: [ '../../../contents.css' ],
  236 + on: {
  237 + contentDom: callback
  238 + }
  239 + } );
  240 +
  241 + CKEDITOR.inline( 'editor2', {
  242 + extraPlugins: 'lineutils',
  243 + removePlugins: 'magicline',
  244 + allowedContent: true,
  245 + contentsCss: [ '../../../contents.css' ],
  246 + on: {
  247 + contentDom: callback
  248 + }
  249 + } );
  250 +
  251 + CKEDITOR.inline( 'editor3', {
  252 + extraPlugins: 'lineutils',
  253 + removePlugins: 'magicline',
  254 + allowedContent: true,
  255 + contentsCss: [ '../../../contents.css' ],
  256 + on: {
  257 + contentDom: callback
  258 + }
  259 + } );
  260 +
  261 + CKEDITOR.replace( 'editor4', {
  262 + extraPlugins: 'lineutils',
  263 + removePlugins: 'magicline',
  264 + allowedContent: true,
  265 + contentsCss: [ '../../../contents.css' ],
  266 + on: {
  267 + contentDom: callback
  268 + }
  269 + } );
  270 +
  271 +
  272 + </script>
  273 +
  274 + <div id="footer">
  275 + <hr>
  276 + <p>
  277 + CKEditor - The text editor for the Internet - <a class="samples" href="http://ckeditor.com/">http://ckeditor.com</a>
  278 + </p>
  279 + <p id="copy">
  280 + Copyright &copy; 2003-2017, <a class="samples" href="http://cksource.com/">CKSource</a> - Frederico
  281 + Knabben. All rights reserved.
  282 + </p>
  283 + </div>
  284 +</body>
  285 +</html>
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +
  6 + /**
  7 + * @fileOverview A set of utilities to find and create horizontal spaces in edited content.
  8 + */
  9 +
  10 +'use strict';
  11 +
  12 +( function() {
  13 +
  14 + CKEDITOR.plugins.add( 'lineutils' );
  15 +
  16 + /**
  17 + * Determines a position relative to an element in DOM (before).
  18 + *
  19 + * @readonly
  20 + * @property {Number} [=0]
  21 + * @member CKEDITOR
  22 + */
  23 + CKEDITOR.LINEUTILS_BEFORE = 1;
  24 +
  25 + /**
  26 + * Determines a position relative to an element in DOM (after).
  27 + *
  28 + * @readonly
  29 + * @property {Number} [=2]
  30 + * @member CKEDITOR
  31 + */
  32 + CKEDITOR.LINEUTILS_AFTER = 2;
  33 +
  34 + /**
  35 + * Determines a position relative to an element in DOM (inside).
  36 + *
  37 + * @readonly
  38 + * @property {Number} [=4]
  39 + * @member CKEDITOR
  40 + */
  41 + CKEDITOR.LINEUTILS_INSIDE = 4;
  42 +
  43 + /**
  44 + * A utility that traverses the DOM tree and discovers elements
  45 + * (relations) matching user-defined lookups.
  46 + *
  47 + * @private
  48 + * @class CKEDITOR.plugins.lineutils.finder
  49 + * @constructor Creates a Finder class instance.
  50 + * @param {CKEDITOR.editor} editor Editor instance that the Finder belongs to.
  51 + * @param {Object} def Finder's definition.
  52 + * @since 4.3
  53 + */
  54 + function Finder( editor, def ) {
  55 + CKEDITOR.tools.extend( this, {
  56 + editor: editor,
  57 + editable: editor.editable(),
  58 + doc: editor.document,
  59 + win: editor.window
  60 + }, def, true );
  61 +
  62 + this.inline = this.editable.isInline();
  63 +
  64 + if ( !this.inline ) {
  65 + this.frame = this.win.getFrame();
  66 + }
  67 +
  68 + this.target = this[ this.inline ? 'editable' : 'doc' ];
  69 + }
  70 +
  71 + Finder.prototype = {
  72 + /**
  73 + * Initializes searching for elements with every mousemove event fired.
  74 + * To stop searching use {@link #stop}.
  75 + *
  76 + * @param {Function} [callback] Function executed on every iteration.
  77 + */
  78 + start: function( callback ) {
  79 + var that = this,
  80 + editor = this.editor,
  81 + doc = this.doc,
  82 + el, elfp, x, y;
  83 +
  84 + var moveBuffer = CKEDITOR.tools.eventsBuffer( 50, function() {
  85 + if ( editor.readOnly || editor.mode != 'wysiwyg' )
  86 + return;
  87 +
  88 + that.relations = {};
  89 +
  90 + // Sometimes it happens that elementFromPoint returns null (especially on IE).
  91 + // Any further traversal makes no sense if there's no start point. Abort.
  92 + // Note: In IE8 elementFromPoint may return zombie nodes of undefined nodeType,
  93 + // so rejecting those as well.
  94 + if ( !( elfp = doc.$.elementFromPoint( x, y ) ) || !elfp.nodeType ) {
  95 + return;
  96 + }
  97 +
  98 + el = new CKEDITOR.dom.element( elfp );
  99 +
  100 + that.traverseSearch( el );
  101 +
  102 + if ( !isNaN( x + y ) ) {
  103 + that.pixelSearch( el, x, y );
  104 + }
  105 +
  106 + callback && callback( that.relations, x, y );
  107 + } );
  108 +
  109 + // Searching starting from element from point on mousemove.
  110 + this.listener = this.editable.attachListener( this.target, 'mousemove', function( evt ) {
  111 + x = evt.data.$.clientX;
  112 + y = evt.data.$.clientY;
  113 +
  114 + moveBuffer.input();
  115 + } );
  116 +
  117 + this.editable.attachListener( this.inline ? this.editable : this.frame, 'mouseout', function() {
  118 + moveBuffer.reset();
  119 + } );
  120 + },
  121 +
  122 + /**
  123 + * Stops observing mouse events attached by {@link #start}.
  124 + */
  125 + stop: function() {
  126 + if ( this.listener ) {
  127 + this.listener.removeListener();
  128 + }
  129 + },
  130 +
  131 + /**
  132 + * Returns a range representing the relation, according to its element
  133 + * and type.
  134 + *
  135 + * @param {Object} location Location containing a unique identifier and type.
  136 + * @returns {CKEDITOR.dom.range} Range representing the relation.
  137 + */
  138 + getRange: ( function() {
  139 + var where = {};
  140 +
  141 + where[ CKEDITOR.LINEUTILS_BEFORE ] = CKEDITOR.POSITION_BEFORE_START;
  142 + where[ CKEDITOR.LINEUTILS_AFTER ] = CKEDITOR.POSITION_AFTER_END;
  143 + where[ CKEDITOR.LINEUTILS_INSIDE ] = CKEDITOR.POSITION_AFTER_START;
  144 +
  145 + return function( location ) {
  146 + var range = this.editor.createRange();
  147 +
  148 + range.moveToPosition( this.relations[ location.uid ].element, where[ location.type ] );
  149 +
  150 + return range;
  151 + };
  152 + } )(),
  153 +
  154 + /**
  155 + * Stores given relation in a {@link #relations} object. Processes the relation
  156 + * to normalize and avoid duplicates.
  157 + *
  158 + * @param {CKEDITOR.dom.element} el Element of the relation.
  159 + * @param {Number} type Relation, one of `CKEDITOR.LINEUTILS_AFTER`, `CKEDITOR.LINEUTILS_BEFORE`, `CKEDITOR.LINEUTILS_INSIDE`.
  160 + */
  161 + store: ( function() {
  162 + function merge( el, type, relations ) {
  163 + var uid = el.getUniqueId();
  164 +
  165 + if ( uid in relations ) {
  166 + relations[ uid ].type |= type;
  167 + } else {
  168 + relations[ uid ] = { element: el, type: type };
  169 + }
  170 + }
  171 +
  172 + return function( el, type ) {
  173 + var alt;
  174 +
  175 + // Normalization to avoid duplicates:
  176 + // CKEDITOR.LINEUTILS_AFTER becomes CKEDITOR.LINEUTILS_BEFORE of el.getNext().
  177 + if ( is( type, CKEDITOR.LINEUTILS_AFTER ) && isStatic( alt = el.getNext() ) && alt.isVisible() ) {
  178 + merge( alt, CKEDITOR.LINEUTILS_BEFORE, this.relations );
  179 + type ^= CKEDITOR.LINEUTILS_AFTER;
  180 + }
  181 +
  182 + // Normalization to avoid duplicates:
  183 + // CKEDITOR.LINEUTILS_INSIDE becomes CKEDITOR.LINEUTILS_BEFORE of el.getFirst().
  184 + if ( is( type, CKEDITOR.LINEUTILS_INSIDE ) && isStatic( alt = el.getFirst() ) && alt.isVisible() ) {
  185 + merge( alt, CKEDITOR.LINEUTILS_BEFORE, this.relations );
  186 + type ^= CKEDITOR.LINEUTILS_INSIDE;
  187 + }
  188 +
  189 + merge( el, type, this.relations );
  190 + };
  191 + } )(),
  192 +
  193 + /**
  194 + * Traverses the DOM tree towards root, checking all ancestors
  195 + * with lookup rules, avoiding duplicates. Stores positive relations
  196 + * in the {@link #relations} object.
  197 + *
  198 + * @param {CKEDITOR.dom.element} el Element which is the starting point.
  199 + */
  200 + traverseSearch: function( el ) {
  201 + var l, type, uid;
  202 +
  203 + // Go down DOM towards root (or limit).
  204 + do {
  205 + uid = el.$[ 'data-cke-expando' ];
  206 +
  207 + // This element was already visited and checked.
  208 + if ( uid && uid in this.relations ) {
  209 + continue;
  210 + }
  211 +
  212 + if ( el.equals( this.editable ) ) {
  213 + return;
  214 + }
  215 +
  216 + if ( isStatic( el ) ) {
  217 + // Collect all addresses yielded by lookups for that element.
  218 + for ( l in this.lookups ) {
  219 +
  220 + if ( ( type = this.lookups[ l ]( el ) ) ) {
  221 + this.store( el, type );
  222 + }
  223 + }
  224 + }
  225 + } while ( !isLimit( el ) && ( el = el.getParent() ) );
  226 + },
  227 +
  228 + /**
  229 + * Iterates vertically pixel-by-pixel within a given element starting
  230 + * from given coordinates, searching for elements in the neighborhood.
  231 + * Once an element is found it is processed by {@link #traverseSearch}.
  232 + *
  233 + * @param {CKEDITOR.dom.element} el Element which is the starting point.
  234 + * @param {Number} [x] Horizontal mouse coordinate relative to the viewport.
  235 + * @param {Number} [y] Vertical mouse coordinate relative to the viewport.
  236 + */
  237 + pixelSearch: ( function() {
  238 + var contains = CKEDITOR.env.ie || CKEDITOR.env.webkit ?
  239 + function( el, found ) {
  240 + return el.contains( found );
  241 + } : function( el, found ) {
  242 + return !!( el.compareDocumentPosition( found ) & 16 );
  243 + };
  244 +
  245 + // Iterates pixel-by-pixel from starting coordinates, moving by defined
  246 + // step and getting elementFromPoint in every iteration. Iteration stops when:
  247 + // * A valid element is found.
  248 + // * Condition function returns `false` (i.e. reached boundaries of viewport).
  249 + // * No element is found (i.e. coordinates out of viewport).
  250 + // * Element found is ascendant of starting element.
  251 + //
  252 + // @param {Object} doc Native DOM document.
  253 + // @param {Object} el Native DOM element.
  254 + // @param {Number} xStart Horizontal starting coordinate to use.
  255 + // @param {Number} yStart Vertical starting coordinate to use.
  256 + // @param {Number} step Step of the algorithm.
  257 + // @param {Function} condition A condition relative to current vertical coordinate.
  258 + function iterate( el, xStart, yStart, step, condition ) {
  259 + var y = yStart,
  260 + tryouts = 0,
  261 + found;
  262 +
  263 + while ( condition( y ) ) {
  264 + y += step;
  265 +
  266 + // If we try and we try, and still nothing's found, let's end
  267 + // that party.
  268 + if ( ++tryouts == 25 ) {
  269 + return;
  270 + }
  271 +
  272 + found = this.doc.$.elementFromPoint( xStart, y );
  273 +
  274 + // Nothing found. This is crazy... but...
  275 + // It might be that a line, which is in different document,
  276 + // covers that pixel (elementFromPoint is doc-sensitive).
  277 + // Better let's have another try.
  278 + if ( !found ) {
  279 + continue;
  280 + }
  281 +
  282 + // Still in the same element.
  283 + else if ( found == el ) {
  284 + tryouts = 0;
  285 + continue;
  286 + }
  287 +
  288 + // Reached the edge of an element and found an ancestor or...
  289 + // A line, that covers that pixel. Better let's have another try.
  290 + else if ( !contains( el, found ) ) {
  291 + continue;
  292 + }
  293 +
  294 + tryouts = 0;
  295 +
  296 + // Found a valid element. Stop iterating.
  297 + if ( isStatic( ( found = new CKEDITOR.dom.element( found ) ) ) ) {
  298 + return found;
  299 + }
  300 + }
  301 + }
  302 +
  303 + return function( el, x, y ) {
  304 + var paneHeight = this.win.getViewPaneSize().height,
  305 +
  306 + // Try to find an element iterating *up* from the starting point.
  307 + neg = iterate.call( this, el.$, x, y, -1, function( y ) {
  308 + return y > 0;
  309 + } ),
  310 +
  311 + // Try to find an element iterating *down* from the starting point.
  312 + pos = iterate.call( this, el.$, x, y, 1, function( y ) {
  313 + return y < paneHeight;
  314 + } );
  315 +
  316 + if ( neg ) {
  317 + this.traverseSearch( neg );
  318 +
  319 + // Iterate towards DOM root until neg is a direct child of el.
  320 + while ( !neg.getParent().equals( el ) ) {
  321 + neg = neg.getParent();
  322 + }
  323 + }
  324 +
  325 + if ( pos ) {
  326 + this.traverseSearch( pos );
  327 +
  328 + // Iterate towards DOM root until pos is a direct child of el.
  329 + while ( !pos.getParent().equals( el ) ) {
  330 + pos = pos.getParent();
  331 + }
  332 + }
  333 +
  334 + // Iterate forwards starting from neg and backwards from
  335 + // pos to harvest all children of el between those elements.
  336 + // Stop when neg and pos meet each other or there's none of them.
  337 + // TODO (?) reduce number of hops forwards/backwards.
  338 + while ( neg || pos ) {
  339 + if ( neg ) {
  340 + neg = neg.getNext( isStatic );
  341 + }
  342 +
  343 + if ( !neg || neg.equals( pos ) ) {
  344 + break;
  345 + }
  346 +
  347 + this.traverseSearch( neg );
  348 +
  349 + if ( pos ) {
  350 + pos = pos.getPrevious( isStatic );
  351 + }
  352 +
  353 + if ( !pos || pos.equals( neg ) ) {
  354 + break;
  355 + }
  356 +
  357 + this.traverseSearch( pos );
  358 + }
  359 + };
  360 + } )(),
  361 +
  362 + /**
  363 + * Unlike {@link #traverseSearch}, it collects **all** elements from editable's DOM tree
  364 + * and runs lookups for every one of them, collecting relations.
  365 + *
  366 + * @returns {Object} {@link #relations}.
  367 + */
  368 + greedySearch: function() {
  369 + this.relations = {};
  370 +
  371 + var all = this.editable.getElementsByTag( '*' ),
  372 + i = 0,
  373 + el, type, l;
  374 +
  375 + while ( ( el = all.getItem( i++ ) ) ) {
  376 + // Don't consider editable, as it might be inline,
  377 + // and i.e. checking it's siblings is pointless.
  378 + if ( el.equals( this.editable ) ) {
  379 + continue;
  380 + }
  381 +
  382 + // On IE8 element.getElementsByTagName returns comments... sic! (http://dev.ckeditor.com/ticket/13176)
  383 + if ( el.type != CKEDITOR.NODE_ELEMENT ) {
  384 + continue;
  385 + }
  386 +
  387 + // Don't visit non-editable internals, for example widget's
  388 + // guts (above wrapper, below nested). Still check editable limits,
  389 + // as they are siblings with editable contents.
  390 + if ( !el.hasAttribute( 'contenteditable' ) && el.isReadOnly() ) {
  391 + continue;
  392 + }
  393 +
  394 + if ( isStatic( el ) && el.isVisible() ) {
  395 + // Collect all addresses yielded by lookups for that element.
  396 + for ( l in this.lookups ) {
  397 + if ( ( type = this.lookups[ l ]( el ) ) ) {
  398 + this.store( el, type );
  399 + }
  400 + }
  401 + }
  402 + }
  403 +
  404 + return this.relations;
  405 + }
  406 +
  407 + /**
  408 + * Relations express elements in DOM that match user-defined {@link #lookups}.
  409 + * Every relation has its own `type` that determines whether
  410 + * it refers to the space before, after or inside the `element`.
  411 + * This object stores relations found by {@link #traverseSearch} or {@link #greedySearch}, structured
  412 + * in the following way:
  413 + *
  414 + * relations: {
  415 + * // Unique identifier of the element.
  416 + * Number: {
  417 + * // Element of this relation.
  418 + * element: {@link CKEDITOR.dom.element}
  419 + * // Conjunction of CKEDITOR.LINEUTILS_BEFORE, CKEDITOR.LINEUTILS_AFTER and CKEDITOR.LINEUTILS_INSIDE.
  420 + * type: Number
  421 + * },
  422 + * ...
  423 + * }
  424 + *
  425 + * @property {Object} relations
  426 + * @readonly
  427 + */
  428 +
  429 + /**
  430 + * A set of user-defined functions used by Finder to check if an element
  431 + * is a valid relation, belonging to {@link #relations}.
  432 + * When the criterion is met, lookup returns a logical conjunction of `CKEDITOR.LINEUTILS_BEFORE`,
  433 + * `CKEDITOR.LINEUTILS_AFTER` or `CKEDITOR.LINEUTILS_INSIDE`.
  434 + *
  435 + * Lookups are passed along with Finder's definition.
  436 + *
  437 + * lookups: {
  438 + * 'some lookup': function( el ) {
  439 + * if ( someCondition )
  440 + * return CKEDITOR.LINEUTILS_BEFORE;
  441 + * },
  442 + * ...
  443 + * }
  444 + *
  445 + * @property {Object} lookups
  446 + */
  447 + };
  448 +
  449 +
  450 + /**
  451 + * A utility that analyses relations found by
  452 + * CKEDITOR.plugins.lineutils.finder and locates them
  453 + * in the viewport as horizontal lines of specific coordinates.
  454 + *
  455 + * @private
  456 + * @class CKEDITOR.plugins.lineutils.locator
  457 + * @constructor Creates a Locator class instance.
  458 + * @param {CKEDITOR.editor} editor Editor instance that Locator belongs to.
  459 + * @since 4.3
  460 + */
  461 + function Locator( editor, def ) {
  462 + CKEDITOR.tools.extend( this, def, {
  463 + editor: editor
  464 + }, true );
  465 + }
  466 +
  467 + Locator.prototype = {
  468 + /**
  469 + * Locates the Y coordinate for all types of every single relation and stores
  470 + * them in an object.
  471 + *
  472 + * @param {Object} relations {@link CKEDITOR.plugins.lineutils.finder#relations}.
  473 + * @returns {Object} {@link #locations}.
  474 + */
  475 + locate: ( function() {
  476 + function locateSibling( rel, type ) {
  477 + var sib = rel.element[ type === CKEDITOR.LINEUTILS_BEFORE ? 'getPrevious' : 'getNext' ]();
  478 +
  479 + // Return the middle point between siblings.
  480 + if ( sib && isStatic( sib ) ) {
  481 + rel.siblingRect = sib.getClientRect();
  482 +
  483 + if ( type == CKEDITOR.LINEUTILS_BEFORE ) {
  484 + return ( rel.siblingRect.bottom + rel.elementRect.top ) / 2;
  485 + } else {
  486 + return ( rel.elementRect.bottom + rel.siblingRect.top ) / 2;
  487 + }
  488 + }
  489 +
  490 + // If there's no sibling, use the edge of an element.
  491 + else {
  492 + if ( type == CKEDITOR.LINEUTILS_BEFORE ) {
  493 + return rel.elementRect.top;
  494 + } else {
  495 + return rel.elementRect.bottom;
  496 + }
  497 + }
  498 + }
  499 +
  500 + return function( relations ) {
  501 + var rel;
  502 +
  503 + this.locations = {};
  504 +
  505 + for ( var uid in relations ) {
  506 + rel = relations[ uid ];
  507 + rel.elementRect = rel.element.getClientRect();
  508 +
  509 + if ( is( rel.type, CKEDITOR.LINEUTILS_BEFORE ) ) {
  510 + this.store( uid, CKEDITOR.LINEUTILS_BEFORE, locateSibling( rel, CKEDITOR.LINEUTILS_BEFORE ) );
  511 + }
  512 +
  513 + if ( is( rel.type, CKEDITOR.LINEUTILS_AFTER ) ) {
  514 + this.store( uid, CKEDITOR.LINEUTILS_AFTER, locateSibling( rel, CKEDITOR.LINEUTILS_AFTER ) );
  515 + }
  516 +
  517 + // The middle point of the element.
  518 + if ( is( rel.type, CKEDITOR.LINEUTILS_INSIDE ) ) {
  519 + this.store( uid, CKEDITOR.LINEUTILS_INSIDE, ( rel.elementRect.top + rel.elementRect.bottom ) / 2 );
  520 + }
  521 + }
  522 +
  523 + return this.locations;
  524 + };
  525 + } )(),
  526 +
  527 + /**
  528 + * Calculates distances from every location to given vertical coordinate
  529 + * and sorts locations according to that distance.
  530 + *
  531 + * @param {Number} y The vertical coordinate used for sorting, used as a reference.
  532 + * @param {Number} [howMany] Determines the number of "closest locations" to be returned.
  533 + * @returns {Array} Sorted, array representation of {@link #locations}.
  534 + */
  535 + sort: ( function() {
  536 + var locations, sorted,
  537 + dist, i;
  538 +
  539 + function distance( y, uid, type ) {
  540 + return Math.abs( y - locations[ uid ][ type ] );
  541 + }
  542 +
  543 + return function( y, howMany ) {
  544 + locations = this.locations;
  545 + sorted = [];
  546 +
  547 + for ( var uid in locations ) {
  548 + for ( var type in locations[ uid ] ) {
  549 + dist = distance( y, uid, type );
  550 +
  551 + // An array is empty.
  552 + if ( !sorted.length ) {
  553 + sorted.push( { uid: +uid, type: type, dist: dist } );
  554 + } else {
  555 + // Sort the array on fly when it's populated.
  556 + for ( i = 0; i < sorted.length; i++ ) {
  557 + if ( dist < sorted[ i ].dist ) {
  558 + sorted.splice( i, 0, { uid: +uid, type: type, dist: dist } );
  559 + break;
  560 + }
  561 + }
  562 +
  563 + // Nothing was inserted, so the distance is bigger than
  564 + // any of already calculated: push to the end.
  565 + if ( i == sorted.length ) {
  566 + sorted.push( { uid: +uid, type: type, dist: dist } );
  567 + }
  568 + }
  569 + }
  570 + }
  571 +
  572 + if ( typeof howMany != 'undefined' ) {
  573 + return sorted.slice( 0, howMany );
  574 + } else {
  575 + return sorted;
  576 + }
  577 + };
  578 + } )(),
  579 +
  580 + /**
  581 + * Stores the location in a collection.
  582 + *
  583 + * @param {Number} uid Unique identifier of the relation.
  584 + * @param {Number} type One of `CKEDITOR.LINEUTILS_BEFORE`, `CKEDITOR.LINEUTILS_AFTER` and `CKEDITOR.LINEUTILS_INSIDE`.
  585 + * @param {Number} y Vertical position of the relation.
  586 + */
  587 + store: function( uid, type, y ) {
  588 + if ( !this.locations[ uid ] ) {
  589 + this.locations[ uid ] = {};
  590 + }
  591 +
  592 + this.locations[ uid ][ type ] = y;
  593 + }
  594 +
  595 + /**
  596 + * @readonly
  597 + * @property {Object} locations
  598 + */
  599 + };
  600 +
  601 + var tipCss = {
  602 + display: 'block',
  603 + width: '0px',
  604 + height: '0px',
  605 + 'border-color': 'transparent',
  606 + 'border-style': 'solid',
  607 + position: 'absolute',
  608 + top: '-6px'
  609 + },
  610 +
  611 + lineStyle = {
  612 + height: '0px',
  613 + 'border-top': '1px dashed red',
  614 + position: 'absolute',
  615 + 'z-index': 9999
  616 + },
  617 +
  618 + lineTpl =
  619 + '<div data-cke-lineutils-line="1" class="cke_reset_all" style="{lineStyle}">' +
  620 + '<span style="{tipLeftStyle}">&nbsp;</span>' +
  621 + '<span style="{tipRightStyle}">&nbsp;</span>' +
  622 + '</div>';
  623 +
  624 + /**
  625 + * A utility that draws horizontal lines in DOM according to locations
  626 + * returned by CKEDITOR.plugins.lineutils.locator.
  627 + *
  628 + * @private
  629 + * @class CKEDITOR.plugins.lineutils.liner
  630 + * @constructor Creates a Liner class instance.
  631 + * @param {CKEDITOR.editor} editor Editor instance that Liner belongs to.
  632 + * @param {Object} def Liner's definition.
  633 + * @since 4.3
  634 + */
  635 + function Liner( editor, def ) {
  636 + var editable = editor.editable();
  637 +
  638 + CKEDITOR.tools.extend( this, {
  639 + editor: editor,
  640 + editable: editable,
  641 + inline: editable.isInline(),
  642 + doc: editor.document,
  643 + win: editor.window,
  644 + container: CKEDITOR.document.getBody(),
  645 + winTop: CKEDITOR.document.getWindow()
  646 + }, def, true );
  647 +
  648 + this.hidden = {};
  649 + this.visible = {};
  650 +
  651 + if ( !this.inline ) {
  652 + this.frame = this.win.getFrame();
  653 + }
  654 +
  655 + this.queryViewport();
  656 +
  657 + // Callbacks must be wrapped. Otherwise they're not attached
  658 + // to global DOM objects (i.e. topmost window) for every editor
  659 + // because they're treated as duplicates. They belong to the
  660 + // same prototype shared among Liner instances.
  661 + var queryViewport = CKEDITOR.tools.bind( this.queryViewport, this ),
  662 + hideVisible = CKEDITOR.tools.bind( this.hideVisible, this ),
  663 + removeAll = CKEDITOR.tools.bind( this.removeAll, this );
  664 +
  665 + editable.attachListener( this.winTop, 'resize', queryViewport );
  666 + editable.attachListener( this.winTop, 'scroll', queryViewport );
  667 +
  668 + editable.attachListener( this.winTop, 'resize', hideVisible );
  669 + editable.attachListener( this.win, 'scroll', hideVisible );
  670 +
  671 + editable.attachListener( this.inline ? editable : this.frame, 'mouseout', function( evt ) {
  672 + var x = evt.data.$.clientX,
  673 + y = evt.data.$.clientY;
  674 +
  675 + this.queryViewport();
  676 +
  677 + // Check if mouse is out of the element (iframe/editable).
  678 + if ( x <= this.rect.left || x >= this.rect.right || y <= this.rect.top || y >= this.rect.bottom ) {
  679 + this.hideVisible();
  680 + }
  681 +
  682 + // Check if mouse is out of the top-window vieport.
  683 + if ( x <= 0 || x >= this.winTopPane.width || y <= 0 || y >= this.winTopPane.height ) {
  684 + this.hideVisible();
  685 + }
  686 + }, this );
  687 +
  688 + editable.attachListener( editor, 'resize', queryViewport );
  689 + editable.attachListener( editor, 'mode', removeAll );
  690 + editor.on( 'destroy', removeAll );
  691 +
  692 + this.lineTpl = new CKEDITOR.template( lineTpl ).output( {
  693 + lineStyle: CKEDITOR.tools.writeCssText(
  694 + CKEDITOR.tools.extend( {}, lineStyle, this.lineStyle, true )
  695 + ),
  696 + tipLeftStyle: CKEDITOR.tools.writeCssText(
  697 + CKEDITOR.tools.extend( {}, tipCss, {
  698 + left: '0px',
  699 + 'border-left-color': 'red',
  700 + 'border-width': '6px 0 6px 6px'
  701 + }, this.tipCss, this.tipLeftStyle, true )
  702 + ),
  703 + tipRightStyle: CKEDITOR.tools.writeCssText(
  704 + CKEDITOR.tools.extend( {}, tipCss, {
  705 + right: '0px',
  706 + 'border-right-color': 'red',
  707 + 'border-width': '6px 6px 6px 0'
  708 + }, this.tipCss, this.tipRightStyle, true )
  709 + )
  710 + } );
  711 + }
  712 +
  713 + Liner.prototype = {
  714 + /**
  715 + * Permanently removes all lines (both hidden and visible) from DOM.
  716 + */
  717 + removeAll: function() {
  718 + var l;
  719 +
  720 + for ( l in this.hidden ) {
  721 + this.hidden[ l ].remove();
  722 + delete this.hidden[ l ];
  723 + }
  724 +
  725 + for ( l in this.visible ) {
  726 + this.visible[ l ].remove();
  727 + delete this.visible[ l ];
  728 + }
  729 + },
  730 +
  731 + /**
  732 + * Hides a given line.
  733 + *
  734 + * @param {CKEDITOR.dom.element} line The line to be hidden.
  735 + */
  736 + hideLine: function( line ) {
  737 + var uid = line.getUniqueId();
  738 +
  739 + line.hide();
  740 +
  741 + this.hidden[ uid ] = line;
  742 + delete this.visible[ uid ];
  743 + },
  744 +
  745 + /**
  746 + * Shows a given line.
  747 + *
  748 + * @param {CKEDITOR.dom.element} line The line to be shown.
  749 + */
  750 + showLine: function( line ) {
  751 + var uid = line.getUniqueId();
  752 +
  753 + line.show();
  754 +
  755 + this.visible[ uid ] = line;
  756 + delete this.hidden[ uid ];
  757 + },
  758 +
  759 + /**
  760 + * Hides all visible lines.
  761 + */
  762 + hideVisible: function() {
  763 + for ( var l in this.visible ) {
  764 + this.hideLine( this.visible[ l ] );
  765 + }
  766 + },
  767 +
  768 + /**
  769 + * Shows a line at given location.
  770 + *
  771 + * @param {Object} location Location object containing the unique identifier of the relation
  772 + * and its type. Usually returned by {@link CKEDITOR.plugins.lineutils.locator#sort}.
  773 + * @param {Function} [callback] A callback to be called once the line is shown.
  774 + */
  775 + placeLine: function( location, callback ) {
  776 + var styles, line, l;
  777 +
  778 + // No style means that line would be out of viewport.
  779 + if ( !( styles = this.getStyle( location.uid, location.type ) ) ) {
  780 + return;
  781 + }
  782 +
  783 + // Search for any visible line of a different hash first.
  784 + // It's faster to re-position visible line than to show it.
  785 + for ( l in this.visible ) {
  786 + if ( this.visible[ l ].getCustomData( 'hash' ) !== this.hash ) {
  787 + line = this.visible[ l ];
  788 + break;
  789 + }
  790 + }
  791 +
  792 + // Search for any hidden line of a different hash.
  793 + if ( !line ) {
  794 + for ( l in this.hidden ) {
  795 + if ( this.hidden[ l ].getCustomData( 'hash' ) !== this.hash ) {
  796 + this.showLine( ( line = this.hidden[ l ] ) );
  797 + break;
  798 + }
  799 + }
  800 + }
  801 +
  802 + // If no line available, add the new one.
  803 + if ( !line ) {
  804 + this.showLine( ( line = this.addLine() ) );
  805 + }
  806 +
  807 + // Mark the line with current hash.
  808 + line.setCustomData( 'hash', this.hash );
  809 +
  810 + // Mark the line as visible.
  811 + this.visible[ line.getUniqueId() ] = line;
  812 +
  813 + line.setStyles( styles );
  814 +
  815 + callback && callback( line );
  816 + },
  817 +
  818 + /**
  819 + * Creates a style set to be used by the line, representing a particular
  820 + * relation (location).
  821 + *
  822 + * @param {Number} uid Unique identifier of the relation.
  823 + * @param {Number} type Type of the relation.
  824 + * @returns {Object} An object containing styles.
  825 + */
  826 + getStyle: function( uid, type ) {
  827 + var rel = this.relations[ uid ],
  828 + loc = this.locations[ uid ][ type ],
  829 + styles = {},
  830 + hdiff;
  831 +
  832 + // Line should be between two elements.
  833 + if ( rel.siblingRect ) {
  834 + styles.width = Math.max( rel.siblingRect.width, rel.elementRect.width );
  835 + }
  836 + // Line is relative to a single element.
  837 + else {
  838 + styles.width = rel.elementRect.width;
  839 + }
  840 +
  841 + // Let's calculate the vertical position of the line.
  842 + if ( this.inline ) {
  843 + // (http://dev.ckeditor.com/ticket/13155)
  844 + styles.top = loc + this.winTopScroll.y - this.rect.relativeY;
  845 + } else {
  846 + styles.top = this.rect.top + this.winTopScroll.y + loc;
  847 + }
  848 +
  849 + // Check if line would be vertically out of the viewport.
  850 + if ( styles.top - this.winTopScroll.y < this.rect.top || styles.top - this.winTopScroll.y > this.rect.bottom ) {
  851 + return false;
  852 + }
  853 +
  854 + // Now let's calculate the horizontal alignment (left and width).
  855 + if ( this.inline ) {
  856 + // (http://dev.ckeditor.com/ticket/13155)
  857 + styles.left = rel.elementRect.left - this.rect.relativeX;
  858 + } else {
  859 + if ( rel.elementRect.left > 0 )
  860 + styles.left = this.rect.left + rel.elementRect.left;
  861 +
  862 + // H-scroll case. Left edge of element may be out of viewport.
  863 + else {
  864 + styles.width += rel.elementRect.left;
  865 + styles.left = this.rect.left;
  866 + }
  867 +
  868 + // H-scroll case. Right edge of element may be out of viewport.
  869 + if ( ( hdiff = styles.left + styles.width - ( this.rect.left + this.winPane.width ) ) > 0 ) {
  870 + styles.width -= hdiff;
  871 + }
  872 + }
  873 +
  874 + // Finally include horizontal scroll of the global window.
  875 + styles.left += this.winTopScroll.x;
  876 +
  877 + // Append 'px' to style values.
  878 + for ( var style in styles ) {
  879 + styles[ style ] = CKEDITOR.tools.cssLength( styles[ style ] );
  880 + }
  881 +
  882 + return styles;
  883 + },
  884 +
  885 + /**
  886 + * Adds a new line to DOM.
  887 + *
  888 + * @returns {CKEDITOR.dom.element} A brand-new line.
  889 + */
  890 + addLine: function() {
  891 + var line = CKEDITOR.dom.element.createFromHtml( this.lineTpl );
  892 +
  893 + line.appendTo( this.container );
  894 +
  895 + return line;
  896 + },
  897 +
  898 + /**
  899 + * Assigns a unique hash to the instance that is later used
  900 + * to tell unwanted lines from new ones. This method **must** be called
  901 + * before a new set of relations is to be visualized so {@link #cleanup}
  902 + * eventually hides obsolete lines. This is because lines
  903 + * are re-used between {@link #placeLine} calls and the number of
  904 + * necessary ones may vary depending on the number of relations.
  905 + *
  906 + * @param {Object} relations {@link CKEDITOR.plugins.lineutils.finder#relations}.
  907 + * @param {Object} locations {@link CKEDITOR.plugins.lineutils.locator#locations}.
  908 + */
  909 + prepare: function( relations, locations ) {
  910 + this.relations = relations;
  911 + this.locations = locations;
  912 + this.hash = Math.random();
  913 + },
  914 +
  915 + /**
  916 + * Hides all visible lines that do not belong to current hash
  917 + * and no longer represent relations (locations).
  918 + *
  919 + * See also: {@link #prepare}.
  920 + */
  921 + cleanup: function() {
  922 + var line;
  923 +
  924 + for ( var l in this.visible ) {
  925 + line = this.visible[ l ];
  926 +
  927 + if ( line.getCustomData( 'hash' ) !== this.hash ) {
  928 + this.hideLine( line );
  929 + }
  930 + }
  931 + },
  932 +
  933 + /**
  934 + * Queries dimensions of the viewport, editable, frame etc.
  935 + * that are used for correct positioning of the line.
  936 + */
  937 + queryViewport: function() {
  938 + this.winPane = this.win.getViewPaneSize();
  939 + this.winTopScroll = this.winTop.getScrollPosition();
  940 + this.winTopPane = this.winTop.getViewPaneSize();
  941 +
  942 + // (http://dev.ckeditor.com/ticket/13155)
  943 + this.rect = this.getClientRect( this.inline ? this.editable : this.frame );
  944 + },
  945 +
  946 + /**
  947 + * Returns `boundingClientRect` of an element, shifted by the position
  948 + * of `container` when the container is not `static` (http://dev.ckeditor.com/ticket/13155).
  949 + *
  950 + * See also: {@link CKEDITOR.dom.element#getClientRect}.
  951 + *
  952 + * @param {CKEDITOR.dom.element} el A DOM element.
  953 + * @returns {Object} A shifted rect, extended by `relativeY` and `relativeX` properties.
  954 + */
  955 + getClientRect: function( el ) {
  956 + var rect = el.getClientRect(),
  957 + relativeContainerDocPosition = this.container.getDocumentPosition(),
  958 + relativeContainerComputedPosition = this.container.getComputedStyle( 'position' );
  959 +
  960 + // Static or not, those values are used to offset the position of the line so they cannot be undefined.
  961 + rect.relativeX = rect.relativeY = 0;
  962 +
  963 + if ( relativeContainerComputedPosition != 'static' ) {
  964 + // Remember the offset used to shift the clientRect.
  965 + rect.relativeY = relativeContainerDocPosition.y;
  966 + rect.relativeX = relativeContainerDocPosition.x;
  967 +
  968 + rect.top -= rect.relativeY;
  969 + rect.bottom -= rect.relativeY;
  970 + rect.left -= rect.relativeX;
  971 + rect.right -= rect.relativeX;
  972 + }
  973 +
  974 + return rect;
  975 + }
  976 + };
  977 +
  978 + function is( type, flag ) {
  979 + return type & flag;
  980 + }
  981 +
  982 + var floats = { left: 1, right: 1, center: 1 },
  983 + positions = { absolute: 1, fixed: 1 };
  984 +
  985 + function isElement( node ) {
  986 + return node && node.type == CKEDITOR.NODE_ELEMENT;
  987 + }
  988 +
  989 + function isFloated( el ) {
  990 + return !!( floats[ el.getComputedStyle( 'float' ) ] || floats[ el.getAttribute( 'align' ) ] );
  991 + }
  992 +
  993 + function isPositioned( el ) {
  994 + return !!positions[ el.getComputedStyle( 'position' ) ];
  995 + }
  996 +
  997 + function isLimit( node ) {
  998 + return isElement( node ) && node.getAttribute( 'contenteditable' ) == 'true';
  999 + }
  1000 +
  1001 + function isStatic( node ) {
  1002 + return isElement( node ) && !isFloated( node ) && !isPositioned( node );
  1003 + }
  1004 +
  1005 + /**
  1006 + * Global namespace storing definitions and global helpers for the Line Utilities plugin.
  1007 + *
  1008 + * @private
  1009 + * @class
  1010 + * @singleton
  1011 + * @since 4.3
  1012 + */
  1013 + CKEDITOR.plugins.lineutils = {
  1014 + finder: Finder,
  1015 + locator: Locator,
  1016 + liner: Liner
  1017 + };
  1018 +} )();
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'af', {
  6 + 'move': 'Klik en trek on te beweeg',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'ar', {
  6 + 'move': 'إضغط و إسحب للتحريك',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'az', {
  6 + 'move': 'Tıklayın və aparın',
  7 + 'label': '%1 vidjet'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'bg', {
  6 + 'move': 'Кликни и влачи, за да преместиш',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'ca', {
  6 + 'move': 'Clicar i arrossegar per moure',
  7 + 'label': '%1 widget'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'cs', {
  6 + 'move': 'Klepněte a táhněte pro přesunutí',
  7 + 'label': 'Ovládací prvek %1'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'cy', {
  6 + 'move': 'Clcio a llusgo i symud',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'da', {
  6 + 'move': 'Klik og træk for at flytte',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'de-ch', {
  6 + 'move': 'Zum Verschieben anwählen und ziehen',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'de', {
  6 + 'move': 'Zum Verschieben anwählen und ziehen',
  7 + 'label': '%1 Steuerelement'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'el', {
  6 + 'move': 'Κάνετε κλικ και σύρετε το ποντίκι για να μετακινήστε',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'en-gb', {
  6 + 'move': 'Click and drag to move',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'en', {
  6 + 'move': 'Click and drag to move',
  7 + 'label': '%1 widget'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'eo', {
  6 + 'move': 'klaki kaj treni por movi',
  7 + 'label': '%1 fenestraĵo'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'es-mx', {
  6 + 'move': 'Presiona y arrastra para mover',
  7 + 'label': '%1 widget'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'es', {
  6 + 'move': 'Dar clic y arrastrar para mover',
  7 + 'label': 'reproductor %1'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'eu', {
  6 + 'move': 'Klikatu eta arrastatu lekuz aldatzeko',
  7 + 'label': '%1 widget'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'fa', {
  6 + 'move': 'کلیک و کشیدن برای جابجایی',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'fi', {
  6 + 'move': 'Siirrä klikkaamalla ja raahaamalla',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'fr', {
  6 + 'move': 'Cliquer et glisser pour déplacer',
  7 + 'label': 'Élément %1'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'gl', {
  6 + 'move': 'Prema e arrastre para mover',
  7 + 'label': 'Trebello %1'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'he', {
  6 + 'move': 'לחץ וגרור להזזה',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'hr', {
  6 + 'move': 'Klikni i povuci za pomicanje',
  7 + 'label': '%1 widget'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'hu', {
  6 + 'move': 'Kattints és húzd a mozgatáshoz',
  7 + 'label': '%1 modul'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'id', {
  6 + 'move': 'Tekan dan geser untuk memindahkan',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'it', {
  6 + 'move': 'Fare clic e trascinare per spostare',
  7 + 'label': 'Widget %1'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'ja', {
  6 + 'move': 'ドラッグして移動',
  7 + 'label': '%1 ウィジェット'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'km', {
  6 + 'move': 'ចុច​ហើយ​ទាញ​ដើម្បី​ផ្លាស់​ទី',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'ko', {
  6 + 'move': '움직이려면 클릭 후 드래그 하세요',
  7 + 'label': '%1 위젯'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'ku', {
  6 + 'move': 'کرتەبکە و ڕایبکێشە بۆ جوڵاندن',
  7 + 'label': '%1 ویجێت'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'lv', {
  6 + 'move': 'Klikšķina un velc, lai pārvietotu',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'nb', {
  6 + 'move': 'Klikk og dra for å flytte',
  7 + 'label': 'Widget %1'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'nl', {
  6 + 'move': 'Klik en sleep om te verplaatsen',
  7 + 'label': '%1 widget'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'no', {
  6 + 'move': 'Klikk og dra for å flytte',
  7 + 'label': 'Widget %1'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'oc', {
  6 + 'move': 'Clicar e lisar per desplaçar',
  7 + 'label': 'Element %1'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'pl', {
  6 + 'move': 'Kliknij i przeciągnij, by przenieść.',
  7 + 'label': 'Widget %1'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'pt-br', {
  6 + 'move': 'Click e arraste para mover',
  7 + 'label': '%1 widget'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'pt', {
  6 + 'move': 'Clique e arraste para mover',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'ru', {
  6 + 'move': 'Нажмите и перетащите, чтобы переместить',
  7 + 'label': '%1 виджет'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'sk', {
  6 + 'move': 'Kliknite a potiahnite pre presunutie',
  7 + 'label': '%1 widget'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'sl', {
  6 + 'move': 'Kliknite in povlecite, da premaknete',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'sq', {
  6 + 'move': 'Kliko dhe tërhiqe për ta lëvizur',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'sv', {
  6 + 'move': 'Klicka och drag för att flytta',
  7 + 'label': '%1-widget'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'tr', {
  6 + 'move': 'Taşımak için, tıklayın ve sürükleyin',
  7 + 'label': '%1 Grafik Beleşeni'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'tt', {
  6 + 'move': 'Күчереп куер өчен басып шудырыгыз',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'ug', {
  6 + 'move': 'يۆتكەشتە چېكىپ سۆرەڭ',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'uk', {
  6 + 'move': 'Клікніть і потягніть для переміщення',
  7 + 'label': '%1 віджет'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'vi', {
  6 + 'move': 'Nhấp chuột và kéo để di chuyển',
  7 + 'label': '%1 widget' // MISSING
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'zh-cn', {
  6 + 'move': '点击并拖拽以移动',
  7 + 'label': '%1 小部件'
  8 +} );
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +CKEDITOR.plugins.setLang( 'widget', 'zh', {
  6 + 'move': '拖曳以移動',
  7 + 'label': '%1 小工具'
  8 +} );
This diff could not be displayed because it is too large.
  1 +/**
  2 + * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3 + * For licensing, see LICENSE.md or http://ckeditor.com/license
  4 + */
  5 +
  6 +/**
  7 + * @fileOverview A plugin created to handle ticket http://dev.ckeditor.com/ticket/11064. While the issue is caused by native WebKit/Blink behaviour,
  8 + * this plugin can be easily detached or modified when the issue is fixed in the browsers without changing the core.
  9 + * When Ctrl/Cmd + A is pressed to select all content it does not work due to a bug in
  10 + * Webkit/Blink if a non-editable element is at the beginning or the end of the content.
  11 + */
  12 +
  13 +( function() {
  14 + 'use strict';
  15 +
  16 + CKEDITOR.plugins.add( 'widgetselection', {
  17 +
  18 + init: function( editor ) {
  19 + if ( CKEDITOR.env.webkit ) {
  20 + var widgetselection = CKEDITOR.plugins.widgetselection;
  21 +
  22 + editor.on( 'contentDom', function( evt ) {
  23 +
  24 + var editor = evt.editor,
  25 + doc = editor.document,
  26 + editable = editor.editable();
  27 +
  28 + editable.attachListener( doc, 'keydown', function( evt ) {
  29 + var data = evt.data.$;
  30 +
  31 + // Ctrl/Cmd + A
  32 + if ( evt.data.getKey() == 65 && ( CKEDITOR.env.mac && data.metaKey || !CKEDITOR.env.mac && data.ctrlKey ) ) {
  33 +
  34 + // Defer the call so the selection is already changed by the pressed keys.
  35 + CKEDITOR.tools.setTimeout( function() {
  36 +
  37 + // Manage filler elements on keydown. If there is no need
  38 + // to add fillers, we need to check and clean previously used once.
  39 + if ( !widgetselection.addFillers( editable ) ) {
  40 + widgetselection.removeFillers( editable );
  41 + }
  42 + }, 0 );
  43 + }
  44 + }, null, null, -1 );
  45 +
  46 + // Check and clean previously used fillers.
  47 + editor.on( 'selectionCheck', function( evt ) {
  48 + widgetselection.removeFillers( evt.editor.editable() );
  49 + } );
  50 +
  51 + // Remove fillers on paste before data gets inserted into editor.
  52 + editor.on( 'paste', function( evt ) {
  53 + evt.data.dataValue = widgetselection.cleanPasteData( evt.data.dataValue );
  54 + } );
  55 +
  56 + if ( 'selectall' in editor.plugins ) {
  57 + widgetselection.addSelectAllIntegration( editor );
  58 + }
  59 + } );
  60 + }
  61 + }
  62 + } );
  63 +
  64 + /**
  65 + * A set of helper methods for the Widget Selection plugin.
  66 + *
  67 + * @property widgetselection
  68 + * @member CKEDITOR.plugins
  69 + * @since 4.6.1
  70 + */
  71 + CKEDITOR.plugins.widgetselection = {
  72 +
  73 + /**
  74 + * The start filler element reference.
  75 + *
  76 + * @property {CKEDITOR.dom.element}
  77 + * @member CKEDITOR.plugins.widgetselection
  78 + * @private
  79 + */
  80 + startFiller: null,
  81 +
  82 + /**
  83 + * The end filler element reference.
  84 + *
  85 + * @property {CKEDITOR.dom.element}
  86 + * @member CKEDITOR.plugins.widgetselection
  87 + * @private
  88 + */
  89 + endFiller: null,
  90 +
  91 + /**
  92 + * An attribute which identifies the filler element.
  93 + *
  94 + * @property {String}
  95 + * @member CKEDITOR.plugins.widgetselection
  96 + * @private
  97 + */
  98 + fillerAttribute: 'data-cke-filler-webkit',
  99 +
  100 + /**
  101 + * The default content of the filler element. Note: The filler needs to have `visible` content.
  102 + * Unprintable elements or empty content do not help as a workaround.
  103 + *
  104 + * @property {String}
  105 + * @member CKEDITOR.plugins.widgetselection
  106 + * @private
  107 + */
  108 + fillerContent: '&nbsp;',
  109 +
  110 + /**
  111 + * Tag name which is used to create fillers.
  112 + *
  113 + * @property {String}
  114 + * @member CKEDITOR.plugins.widgetselection
  115 + * @private
  116 + */
  117 + fillerTagName: 'div',
  118 +
  119 + /**
  120 + * Adds a filler before or after a non-editable element at the beginning or the end of the `editable`.
  121 + *
  122 + * @param {CKEDITOR.editable} editable
  123 + * @returns {Boolean}
  124 + * @member CKEDITOR.plugins.widgetselection
  125 + */
  126 + addFillers: function( editable ) {
  127 + var editor = editable.editor;
  128 +
  129 + // Whole content should be selected, if not fix the selection manually.
  130 + if ( !this.isWholeContentSelected( editable ) && editable.getChildCount() > 0 ) {
  131 +
  132 + var firstChild = editable.getFirst( filterTempElements ),
  133 + lastChild = editable.getLast( filterTempElements );
  134 +
  135 + // Check if first element is editable. If not prepend with filler.
  136 + if ( firstChild && firstChild.type == CKEDITOR.NODE_ELEMENT && !firstChild.isEditable() ) {
  137 + this.startFiller = this.createFiller();
  138 + editable.append( this.startFiller, 1 );
  139 + }
  140 +
  141 + // Check if last element is editable. If not append filler.
  142 + if ( lastChild && lastChild.type == CKEDITOR.NODE_ELEMENT && !lastChild.isEditable() ) {
  143 + this.endFiller = this.createFiller( true );
  144 + editable.append( this.endFiller, 0 );
  145 + }
  146 +
  147 + // Reselect whole content after any filler was added.
  148 + if ( this.hasFiller( editable ) ) {
  149 + var rangeAll = editor.createRange();
  150 + rangeAll.selectNodeContents( editable );
  151 + rangeAll.select();
  152 + return true;
  153 + }
  154 + }
  155 + return false;
  156 + },
  157 +
  158 + /**
  159 + * Removes filler elements or updates their references.
  160 + *
  161 + * It will **not remove** filler elements if the whole content is selected, as it would break the
  162 + * selection.
  163 + *
  164 + * @param {CKEDITOR.editable} editable
  165 + * @member CKEDITOR.plugins.widgetselection
  166 + */
  167 + removeFillers: function( editable ) {
  168 + // If startFiller or endFiller exists and not entire content is selected it means the selection
  169 + // just changed from selected all. We need to remove fillers and set proper selection/content.
  170 + if ( this.hasFiller( editable ) && !this.isWholeContentSelected( editable ) ) {
  171 +
  172 + var startFillerContent = editable.findOne( this.fillerTagName + '[' + this.fillerAttribute + '=start]' ),
  173 + endFillerContent = editable.findOne( this.fillerTagName + '[' + this.fillerAttribute + '=end]' );
  174 +
  175 + if ( this.startFiller && startFillerContent && this.startFiller.equals( startFillerContent ) ) {
  176 + this.removeFiller( this.startFiller, editable );
  177 + } else {
  178 + // The start filler is still present but it is a different element than previous one. It means the
  179 + // undo recreating entirely selected content was performed. We need to update filler reference.
  180 + this.startFiller = startFillerContent;
  181 + }
  182 +
  183 + if ( this.endFiller && endFillerContent && this.endFiller.equals( endFillerContent ) ) {
  184 + this.removeFiller( this.endFiller, editable );
  185 + } else {
  186 + // Same as with start filler.
  187 + this.endFiller = endFillerContent;
  188 + }
  189 + }
  190 + },
  191 +
  192 + /**
  193 + * Removes fillers from the paste data.
  194 + *
  195 + * @param {String} data
  196 + * @returns {String}
  197 + * @member CKEDITOR.plugins.widgetselection
  198 + * @private
  199 + */
  200 + cleanPasteData: function( data ) {
  201 + if ( data && data.length ) {
  202 + data = data
  203 + .replace( this.createFillerRegex(), '' )
  204 + .replace( this.createFillerRegex( true ), '' );
  205 + }
  206 + return data;
  207 + },
  208 +
  209 + /**
  210 + * Checks if the entire content of the given editable is selected.
  211 + *
  212 + * @param {CKEDITOR.editable} editable
  213 + * @returns {Boolean}
  214 + * @member CKEDITOR.plugins.widgetselection
  215 + * @private
  216 + */
  217 + isWholeContentSelected: function( editable ) {
  218 +
  219 + var range = editable.editor.getSelection().getRanges()[ 0 ];
  220 + if ( range ) {
  221 +
  222 + if ( range && range.collapsed ) {
  223 + return false;
  224 +
  225 + } else {
  226 + var rangeClone = range.clone();
  227 + rangeClone.enlarge( CKEDITOR.ENLARGE_ELEMENT );
  228 +
  229 + return !!( rangeClone && editable && rangeClone.startContainer && rangeClone.endContainer &&
  230 + rangeClone.startOffset === 0 && rangeClone.endOffset === editable.getChildCount() &&
  231 + rangeClone.startContainer.equals( editable ) && rangeClone.endContainer.equals( editable ) );
  232 + }
  233 + }
  234 + return false;
  235 + },
  236 +
  237 + /**
  238 + * Checks if there is any filler element in the given editable.
  239 + *
  240 + * @param {CKEDITOR.editable} editable
  241 + * @returns {Boolean}
  242 + * @member CKEDITOR.plugins.widgetselection
  243 + * @private
  244 + */
  245 + hasFiller: function( editable ) {
  246 + return editable.find( this.fillerTagName + '[' + this.fillerAttribute + ']' ).count() > 0;
  247 + },
  248 +
  249 + /**
  250 + * Creates a filler element.
  251 + *
  252 + * @param {Boolean} [onEnd] If filler will be placed on end or beginning of the content.
  253 + * @returns {CKEDITOR.dom.element}
  254 + * @member CKEDITOR.plugins.widgetselection
  255 + * @private
  256 + */
  257 + createFiller: function( onEnd ) {
  258 + var filler = new CKEDITOR.dom.element( this.fillerTagName );
  259 + filler.setHtml( this.fillerContent );
  260 + filler.setAttribute( this.fillerAttribute, onEnd ? 'end' : 'start' );
  261 + filler.setAttribute( 'data-cke-temp', 1 );
  262 + filler.setStyles( {
  263 + display: 'block',
  264 + width: 0,
  265 + height: 0,
  266 + padding: 0,
  267 + border: 0,
  268 + margin: 0,
  269 + position: 'absolute',
  270 + top: 0,
  271 + left: '-9999px',
  272 + opacity: 0,
  273 + overflow: 'hidden'
  274 + } );
  275 +
  276 + return filler;
  277 + },
  278 +
  279 + /**
  280 + * Removes the specific filler element from the given editable. If the filler contains any content (typed or pasted),
  281 + * it replaces the current editable content. If not, the caret is placed before the first or after the last editable
  282 + * element (depends if the filler was at the beginning or the end).
  283 + *
  284 + * @param {CKEDITOR.dom.element} filler
  285 + * @param {CKEDITOR.editable} editable
  286 + * @member CKEDITOR.plugins.widgetselection
  287 + * @private
  288 + */
  289 + removeFiller: function( filler, editable ) {
  290 + if ( filler ) {
  291 + var editor = editable.editor,
  292 + currentRange = editable.editor.getSelection().getRanges()[ 0 ],
  293 + currentPath = currentRange.startPath(),
  294 + range = editor.createRange(),
  295 + insertedHtml,
  296 + fillerOnStart,
  297 + manuallyHandleCaret;
  298 +
  299 + if ( currentPath.contains( filler ) ) {
  300 + insertedHtml = filler.getHtml();
  301 + manuallyHandleCaret = true;
  302 + }
  303 +
  304 + fillerOnStart = filler.getAttribute( this.fillerAttribute ) == 'start';
  305 + filler.remove();
  306 + filler = null;
  307 +
  308 + if ( insertedHtml && insertedHtml.length > 0 && insertedHtml != this.fillerContent ) {
  309 + editable.insertHtmlIntoRange( insertedHtml, editor.getSelection().getRanges()[ 0 ] );
  310 + range.setStartAt( editable.getChild( editable.getChildCount() - 1 ), CKEDITOR.POSITION_BEFORE_END );
  311 + editor.getSelection().selectRanges( [ range ] );
  312 +
  313 + } else if ( manuallyHandleCaret ) {
  314 + if ( fillerOnStart ) {
  315 + range.setStartAt( editable.getFirst().getNext(), CKEDITOR.POSITION_AFTER_START );
  316 + } else {
  317 + range.setEndAt( editable.getLast().getPrevious(), CKEDITOR.POSITION_BEFORE_END );
  318 + }
  319 + editable.editor.getSelection().selectRanges( [ range ] );
  320 + }
  321 + }
  322 + },
  323 +
  324 + /**
  325 + * Creates a regular expression which will match the filler HTML in the text.
  326 + *
  327 + * @param {Boolean} [onEnd] Whether a regular expression should be created for the filler at the beginning or
  328 + * the end of the content.
  329 + * @returns {RegExp}
  330 + * @member CKEDITOR.plugins.widgetselection
  331 + * @private
  332 + */
  333 + createFillerRegex: function( onEnd ) {
  334 + var matcher = this.createFiller( onEnd ).getOuterHtml()
  335 + .replace( /style="[^"]*"/gi, 'style="[^"]*"' )
  336 + .replace( />[^<]*</gi, '>[^<]*<' );
  337 +
  338 + return new RegExp( ( !onEnd ? '^' : '' ) + matcher + ( onEnd ? '$' : '' ) );
  339 + },
  340 +
  341 + /**
  342 + * Adds an integration for the [Select All](http://ckeditor.com/addon/selectall) plugin to the given `editor`.
  343 + *
  344 + * @private
  345 + * @param {CKEDITOR.editor} editor
  346 + * @member CKEDITOR.plugins.widgetselection
  347 + */
  348 + addSelectAllIntegration: function( editor ) {
  349 + var widgetselection = this;
  350 +
  351 + editor.editable().attachListener( editor, 'beforeCommandExec', function( evt ) {
  352 + var editable = editor.editable();
  353 +
  354 + if ( evt.data.name == 'selectAll' && editable ) {
  355 + widgetselection.addFillers( editable );
  356 + }
  357 + }, null, null, 9999 );
  358 + }
  359 + };
  360 +
  361 +
  362 + function filterTempElements( el ) {
  363 + return el.getName && !el.hasAttribute( 'data-cke-temp' );
  364 + }
  365 +
  366 +} )();