embedapi.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. Embedded SVG-edit API
  3. General usage:
  4. - Have an iframe somewhere pointing to a version of svg-edit > r1000
  5. - Initialize the magic with:
  6. var svgCanvas = new EmbeddedSVGEdit(window.frames.svgedit);
  7. - Pass functions in this format:
  8. svgCanvas.setSvgString('string')
  9. - Or if a callback is needed:
  10. svgCanvas.setSvgString('string')(function(data, error){
  11. if (error){
  12. // There was an error
  13. } else{
  14. // Handle data
  15. }
  16. })
  17. Everything is done with the same API as the real svg-edit,
  18. and all documentation is unchanged.
  19. However, this file depends on the postMessage API which
  20. can only support JSON-serializable arguments and
  21. return values, so, for example, arguments whose value is
  22. 'undefined', a function, a non-finite number, or a built-in
  23. object like Date(), RegExp(), etc. will most likely not behave
  24. as expected. In such a case one may need to host
  25. the SVG editor on the same domain and reference the
  26. JavaScript methods on the frame itself.
  27. The only other difference is
  28. when handling returns: the callback notation is used instead.
  29. var blah = new EmbeddedSVGEdit(window.frames.svgedit);
  30. blah.clearSelection('woot', 'blah', 1337, [1, 2, 3, 4, 5, 'moo'], -42, {a: 'tree',b:6, c: 9})(function(){console.log('GET DATA',arguments)})
  31. */
  32. (function () {'use strict';
  33. var cbid = 0;
  34. function getCallbackSetter (d) {
  35. return function () {
  36. var t = this, // New callback
  37. args = [].slice.call(arguments),
  38. cbid = t.send(d, args, function(){}); // The callback (currently it's nothing, but will be set later)
  39. return function(newcallback){
  40. t.callbacks[cbid] = newcallback; // Set callback
  41. };
  42. };
  43. }
  44. /*
  45. * Having this separate from messageListener allows us to
  46. * avoid using JSON parsing (and its limitations) in the case
  47. * of same domain control
  48. */
  49. function addCallback (t, data) {
  50. var result = data.result || data.error,
  51. cbid = data.id;
  52. if (t.callbacks[cbid]) {
  53. if (data.result) {
  54. t.callbacks[cbid](result);
  55. } else {
  56. t.callbacks[cbid](result, 'error');
  57. }
  58. }
  59. }
  60. function messageListener (e) {
  61. // We accept and post strings as opposed to objects for the sake of IE9 support; this
  62. // will most likely be changed in the future
  63. if (typeof e.data !== 'string') {
  64. return;
  65. }
  66. var allowedOrigins = this.allowedOrigins,
  67. data = e.data && JSON.parse(e.data);
  68. if (!data || typeof data !== 'object' || data.namespace !== 'svg-edit' ||
  69. e.source !== this.frame.contentWindow ||
  70. (allowedOrigins.indexOf('*') === -1 && allowedOrigins.indexOf(e.origin) === -1)
  71. ) {
  72. return;
  73. }
  74. addCallback(this, data);
  75. }
  76. function getMessageListener (t) {
  77. return function (e) {
  78. messageListener.call(t, e);
  79. };
  80. }
  81. /**
  82. * @param {HTMLIFrameElement} frame
  83. * @param {array} [allowedOrigins=[]] Array of origins from which incoming
  84. * messages will be allowed when same origin is not used; defaults to none.
  85. * If supplied, it should probably be the same as svgEditor's allowedOrigins
  86. */
  87. function EmbeddedSVGEdit (frame, allowedOrigins) {
  88. if (!(this instanceof EmbeddedSVGEdit)) { // Allow invocation without 'new' keyword
  89. return new EmbeddedSVGEdit(frame);
  90. }
  91. this.allowedOrigins = allowedOrigins || [];
  92. // Initialize communication
  93. this.frame = frame;
  94. this.callbacks = {};
  95. // List of functions extracted with this:
  96. // Run in firebug on http://svg-edit.googlecode.com/svn/trunk/docs/files/svgcanvas-js.html
  97. // for (var i=0,q=[],f = document.querySelectorAll('div.CFunction h3.CTitle a'); i < f.length; i++) { q.push(f[i].name); }; q
  98. // var functions = ['clearSelection', 'addToSelection', 'removeFromSelection', 'open', 'save', 'getSvgString', 'setSvgString',
  99. // 'createLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility',
  100. // 'moveSelectedToLayer', 'clear'];
  101. // Newer, well, it extracts things that aren't documented as well. All functions accessible through the normal thingy can now be accessed though the API
  102. // var svgCanvas = frame.contentWindow.svgCanvas;
  103. // var l = []; for (var i in svgCanvas){ if (typeof svgCanvas[i] == 'function') { l.push(i);} };
  104. // alert("['" + l.join("', '") + "']");
  105. // Run in svgedit itself
  106. var i,
  107. functions = [
  108. 'clearSvgContentElement', 'setIdPrefix', 'getCurrentDrawing', 'addSvgElementFromJson', 'getTransformList', 'matrixMultiply', 'hasMatrixTransform', 'transformListToTransform', 'convertToNum', 'findDefs', 'getUrlFromAttr', 'getHref', 'setHref', 'getBBox', 'getRotationAngle', 'getElem', 'getRefElem', 'assignAttributes', 'cleanupElement', 'remapElement', 'recalculateDimensions', 'sanitizeSvg', 'runExtensions', 'addExtension', 'round', 'getIntersectionList', 'getStrokedBBox', 'getVisibleElements', 'getVisibleElementsAndBBoxes', 'groupSvgElem', 'getId', 'getNextId', 'call', 'bind', 'prepareSvg', 'setRotationAngle', 'recalculateAllSelectedDimensions', 'clearSelection', 'addToSelection', 'selectOnly', 'removeFromSelection', 'selectAllInCurrentLayer', 'getMouseTarget', 'removeUnusedDefElems', 'svgCanvasToString', 'svgToString', 'embedImage', 'setGoodImage', 'open', 'save', 'rasterExport', 'getSvgString', 'randomizeIds', 'uniquifyElems', 'setUseData', 'convertGradients', 'convertToGroup', 'setSvgString', 'importSvgString', 'identifyLayers', 'createLayer', 'cloneLayer', 'deleteCurrentLayer', 'setCurrentLayer', 'renameCurrentLayer', 'setCurrentLayerPosition', 'setLayerVisibility', 'moveSelectedToLayer', 'mergeLayer', 'mergeAllLayers', 'leaveContext', 'setContext', 'clear', 'linkControlPoints', 'getContentElem', 'getRootElem', 'getSelectedElems', 'getResolution', 'getZoom', 'getVersion', 'setUiStrings', 'setConfig', 'getTitle', 'setGroupTitle', 'getDocumentTitle', 'setDocumentTitle', 'getEditorNS', 'setResolution', 'getOffset', 'setBBoxZoom', 'setZoom', 'getMode', 'setMode', 'getColor', 'setColor', 'setGradient', 'setPaint', 'setStrokePaint', 'setFillPaint', 'getStrokeWidth', 'setStrokeWidth', 'setStrokeAttr', 'getStyle', 'getOpacity', 'setOpacity', 'getFillOpacity', 'getStrokeOpacity', 'setPaintOpacity', 'getPaintOpacity', 'getBlur', 'setBlurNoUndo', 'setBlurOffsets', 'setBlur', 'getBold', 'setBold', 'getItalic', 'setItalic', 'getFontFamily', 'setFontFamily', 'setFontColor', 'getFontColor', 'getFontSize', 'setFontSize', 'getText', 'setTextContent', 'setImageURL', 'setLinkURL', 'setRectRadius', 'makeHyperlink', 'removeHyperlink', 'setSegType', 'convertToPath', 'changeSelectedAttribute', 'deleteSelectedElements', 'cutSelectedElements', 'copySelectedElements', 'pasteElements', 'groupSelectedElements', 'pushGroupProperties', 'ungroupSelectedElement', 'moveToTopSelectedElement', 'moveToBottomSelectedElement', 'moveUpDownSelected', 'moveSelectedElements', 'cloneSelectedElements', 'alignSelectedElements', 'updateCanvas', 'setBackground', 'cycleElement', 'getPrivateMethods', 'zoomChanged', 'ready'
  109. ];
  110. // TODO: rewrite the following, it's pretty scary.
  111. for (i = 0; i < functions.length; i++) {
  112. this[functions[i]] = getCallbackSetter(functions[i]);
  113. }
  114. // Older IE may need a polyfill for addEventListener, but so it would for SVG
  115. window.addEventListener('message', getMessageListener(this), false);
  116. }
  117. EmbeddedSVGEdit.prototype.send = function (name, args, callback){
  118. var t = this;
  119. cbid++;
  120. this.callbacks[cbid] = callback;
  121. setTimeout((function (cbid) {
  122. return function () { // Delay for the callback to be set in case its synchronous
  123. /*
  124. * Todo: Handle non-JSON arguments and return values (undefined,
  125. * nonfinite numbers, functions, and built-in objects like Date,
  126. * RegExp), etc.? Allow promises instead of callbacks? Review
  127. * SVG-Edit functions for whether JSON-able parameters can be
  128. * made compatile with all API functionality
  129. */
  130. // We accept and post strings for the sake of IE9 support
  131. if (window.location.origin === t.frame.contentWindow.location.origin) {
  132. // Although we do not really need this API if we are working same
  133. // domain, it could allow us to write in a way that would work
  134. // cross-domain as well, assuming we stick to the argument limitations
  135. // of the current JSON-based communication API (e.g., not passing
  136. // callbacks). We might be able to address these shortcomings; see
  137. // the todo elsewhere in this file.
  138. var message = {id: cbid},
  139. svgCanvas = t.frame.contentWindow.svgCanvas;
  140. try {
  141. message.result = svgCanvas[name].apply(svgCanvas, args);
  142. }
  143. catch (err) {
  144. message.error = err.message;
  145. }
  146. addCallback(t, message);
  147. }
  148. else { // Requires the ext-xdomain-messaging.js extension
  149. t.frame.contentWindow.postMessage(JSON.stringify({namespace: 'svgCanvas', id: cbid, name: name, args: args}), '*');
  150. }
  151. };
  152. }(cbid)), 0);
  153. return cbid;
  154. };
  155. window.embedded_svg_edit = EmbeddedSVGEdit; // Export old, deprecated API
  156. window.EmbeddedSVGEdit = EmbeddedSVGEdit; // Follows common JS convention of CamelCase and, as enforced in JSLint, of initial caps for constructors
  157. }());