/src/core/PaperScope.js

http://github.com/paperjs/paper.js · JavaScript · 324 lines · 121 code · 21 blank · 182 comment · 18 complexity · 96b6046e5960cb935c6fbc33f84e62dc MD5 · raw file

  1. /*
  2. * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
  3. * http://paperjs.org/
  4. *
  5. * Copyright (c) 2011 - 2020, Jürg Lehni & Jonathan Puckey
  6. * http://juerglehni.com/ & https://puckey.studio/
  7. *
  8. * Distributed under the MIT license. See LICENSE file for details.
  9. *
  10. * All rights reserved.
  11. */
  12. /**
  13. * @name PaperScope
  14. *
  15. * @class The `PaperScope` class represents the scope associated with a Paper
  16. * context. When working with PaperScript, these scopes are automatically
  17. * created for us, and through clever scoping the properties and methods of
  18. * the active scope seem to become part of the global scope.
  19. *
  20. * When working with normal JavaScript code, `PaperScope` objects need to be
  21. * manually created and handled.
  22. *
  23. * Paper classes can only be accessed through `PaperScope` objects. Thus in
  24. * PaperScript they are global, while in JavaScript, they are available on the
  25. * global {@link paper} object. For JavaScript you can use {@link
  26. * PaperScope#install(scope) } to install the Paper classes and objects on the
  27. * global scope. Note that when working with more than one scope, this still
  28. * works for classes, but not for objects like {@link PaperScope#project}, since
  29. * they are not updated in the injected scope if scopes are switched.
  30. *
  31. * The global {@link paper} object is simply a reference to the currently active
  32. * `PaperScope`.
  33. */
  34. var PaperScope = Base.extend(/** @lends PaperScope# */{
  35. _class: 'PaperScope',
  36. /**
  37. * Creates a PaperScope object.
  38. *
  39. * @name PaperScope#initialize
  40. * @function
  41. */
  42. // DOCS: initialize() parameters
  43. initialize: function PaperScope() {
  44. // element is only used internally when creating scopes for PaperScript.
  45. // Whenever a PaperScope is created, it automatically becomes the active
  46. // one.
  47. paper = this;
  48. // Default configurable settings.
  49. this.settings = new Base({
  50. applyMatrix: true,
  51. insertItems: true,
  52. handleSize: 4,
  53. hitTolerance: 0
  54. });
  55. this.project = null;
  56. this.projects = [];
  57. this.tools = [];
  58. // Assign a unique id to each scope .
  59. this._id = PaperScope._id++;
  60. PaperScope._scopes[this._id] = this;
  61. var proto = PaperScope.prototype;
  62. if (!this.support) {
  63. // Set up paper.support, as an object containing properties that
  64. // describe the support of various features.
  65. var ctx = CanvasProvider.getContext(1, 1) || {};
  66. proto.support = {
  67. nativeDash: 'setLineDash' in ctx || 'mozDash' in ctx,
  68. nativeBlendModes: BlendMode.nativeModes
  69. };
  70. CanvasProvider.release(ctx);
  71. }
  72. if (!this.agent) {
  73. // Use self.instead of window, to cover handle web-workers too.
  74. var user = self.navigator.userAgent.toLowerCase(),
  75. // Detect basic platforms, only mac internally required for now.
  76. os = (/(darwin|win|mac|linux|freebsd|sunos)/.exec(user)||[])[0],
  77. platform = os === 'darwin' ? 'mac' : os,
  78. agent = proto.agent = proto.browser = { platform: platform };
  79. if (platform)
  80. agent[platform] = true;
  81. // Use replace() to get all matches, and deal with Chrome/Webkit
  82. // overlap:
  83. // TODO: Do we need Mozilla next to Firefox? Other than the
  84. // different treatment of the Chrome/Webkit overlap
  85. // here: { chrome: true, webkit: false }, Mozilla missing is the
  86. // only difference to jQuery.browser
  87. user.replace(
  88. /(opera|chrome|safari|webkit|firefox|msie|trident|atom|node|jsdom)\/?\s*([.\d]+)(?:.*version\/([.\d]+))?(?:.*rv\:v?([.\d]+))?/g,
  89. function(match, n, v1, v2, rv) {
  90. // Do not set additional browsers once chrome is detected.
  91. if (!agent.chrome) {
  92. var v = n === 'opera' ? v2 :
  93. /^(node|trident)$/.test(n) ? rv : v1;
  94. agent.version = v;
  95. agent.versionNumber = parseFloat(v);
  96. n = { trident: 'msie', jsdom: 'node' }[n] || n;
  97. agent.name = n;
  98. agent[n] = true;
  99. }
  100. }
  101. );
  102. if (agent.chrome)
  103. delete agent.webkit;
  104. if (agent.atom)
  105. delete agent.chrome;
  106. }
  107. },
  108. /**
  109. * The version of Paper.js, as a string.
  110. *
  111. * @type String
  112. * @readonly
  113. */
  114. version: /*#=*/__options.version,
  115. /**
  116. * Gives access to paper's configurable settings.
  117. *
  118. * @name PaperScope#settings
  119. * @type Object
  120. *
  121. * @option [settings.insertItems=true] {Boolean} controls whether newly
  122. * created items are automatically inserted into the scene graph, by
  123. * adding them to {@link Project#activeLayer}
  124. * @option [settings.applyMatrix=true] {Boolean} controls what value newly
  125. * created items have their {@link Item#applyMatrix} property set to
  126. * (Note that not all items can set this to `false`)
  127. * @option [settings.handleSize=4] {Number} the size of the curve handles
  128. * when drawing selections
  129. * @option [settings.hitTolerance=0] {Number} the default tolerance for hit-
  130. * tests, when no value is specified
  131. */
  132. /**
  133. * The currently active project.
  134. *
  135. * @name PaperScope#project
  136. * @type Project
  137. */
  138. /**
  139. * The list of all open projects within the current Paper.js context.
  140. *
  141. * @name PaperScope#projects
  142. * @type Project[]
  143. */
  144. /**
  145. * The reference to the active project's view.
  146. *
  147. * @bean
  148. * @type View
  149. */
  150. getView: function() {
  151. var project = this.project;
  152. return project && project._view;
  153. },
  154. /**
  155. * The reference to the active tool.
  156. *
  157. * @name PaperScope#tool
  158. * @property
  159. * @type Tool
  160. */
  161. /**
  162. * The list of available tools.
  163. *
  164. * @name PaperScope#tools
  165. * @property
  166. * @type Tool[]
  167. */
  168. /**
  169. * A reference to the local scope. This is required, so `paper` will always
  170. * refer to the local scope, even when calling into it from another scope.
  171. * `paper.activate();` will have to be called in such a situation.
  172. *
  173. * @bean
  174. * @type PaperScript
  175. * @private
  176. */
  177. getPaper: function() {
  178. return this;
  179. },
  180. /**
  181. * Compiles the PaperScript code into a compiled function and executes it.
  182. * The compiled function receives all properties of this {@link PaperScope}
  183. * as arguments, to emulate a global scope with unaffected performance. It
  184. * also installs global view and tool handlers automatically on the
  185. * respective objects.
  186. *
  187. * @option options.url {String} the url of the source, for source-map
  188. * debugging
  189. * @option options.source {String} the source to be used for the source-
  190. * mapping, in case the code that's passed in has already been mingled.
  191. *
  192. * @param {String} code the PaperScript code
  193. * @param {Object} [options] the compilation options
  194. */
  195. execute: function(code, options) {
  196. /*#*/ if (__options.paperScript) {
  197. var exports = paper.PaperScript.execute(code, this, options);
  198. View.updateFocus();
  199. return exports;
  200. /*#*/ }
  201. },
  202. /**
  203. * Injects the paper scope into any other given scope. Can be used for
  204. * example to inject the currently active PaperScope into the window's
  205. * global scope, to emulate PaperScript-style globally accessible Paper
  206. * classes and objects.
  207. *
  208. * <b>Please note:</b> Using this method may override native constructors
  209. * (e.g. Path). This may cause problems when using Paper.js in conjunction
  210. * with other libraries that rely on these constructors. Keep the library
  211. * scoped if you encounter issues caused by this.
  212. *
  213. * @example
  214. * paper.install(window);
  215. */
  216. install: function(scope) {
  217. // Define project, view and tool as getters that redirect to these
  218. // values on the PaperScope, so they are kept up to date
  219. var that = this;
  220. Base.each(['project', 'view', 'tool'], function(key) {
  221. Base.define(scope, key, {
  222. configurable: true,
  223. get: function() {
  224. return that[key];
  225. }
  226. });
  227. });
  228. // Copy over all fields from this scope to the destination.
  229. // Do not use Base.each, since we also want to enumerate over
  230. // fields on PaperScope.prototype, e.g. all classes
  231. for (var key in this)
  232. // Exclude all 'hidden' fields
  233. if (!/^_/.test(key) && this[key])
  234. scope[key] = this[key];
  235. },
  236. /**
  237. * Sets up an empty project for us. If a canvas is provided, it also creates
  238. * a {@link View} for it, both linked to this scope.
  239. *
  240. * @param {HTMLCanvasElement|String|Size} element the HTML canvas element
  241. * this scope should be associated with, or an ID string by which to find
  242. * the element, or the size of the canvas to be created for usage in a web
  243. * worker.
  244. */
  245. setup: function(element) {
  246. // Make sure this is the active scope, so the created project and view
  247. // are automatically associated with it.
  248. paper = this;
  249. // Create an empty project for the scope.
  250. this.project = new Project(element);
  251. // This is needed in PaperScript.load().
  252. return this;
  253. },
  254. createCanvas: function(width, height) {
  255. return CanvasProvider.getCanvas(width, height);
  256. },
  257. /**
  258. * Activates this PaperScope, so all newly created items will be placed
  259. * in its active project.
  260. */
  261. activate: function() {
  262. paper = this;
  263. },
  264. clear: function() {
  265. // Remove all projects, views and tools.
  266. // This also removes the installed event handlers.
  267. var projects = this.projects,
  268. tools = this.tools;
  269. for (var i = projects.length - 1; i >= 0; i--)
  270. projects[i].remove();
  271. for (var i = tools.length - 1; i >= 0; i--)
  272. tools[i].remove();
  273. },
  274. remove: function() {
  275. this.clear();
  276. delete PaperScope._scopes[this._id];
  277. },
  278. statics: new function() {
  279. // Produces helpers to e.g. check for both 'canvas' and
  280. // 'data-paper-canvas' attributes:
  281. function handleAttribute(name) {
  282. name += 'Attribute';
  283. return function(el, attr) {
  284. return el[name](attr) || el[name]('data-paper-' + attr);
  285. };
  286. }
  287. return /** @lends PaperScope */{
  288. _scopes: {},
  289. _id: 0,
  290. /**
  291. * Retrieves a PaperScope object with the given scope id.
  292. *
  293. * @param id
  294. * @return {PaperScope}
  295. */
  296. get: function(id) {
  297. return this._scopes[id] || null;
  298. },
  299. getAttribute: handleAttribute('get'),
  300. hasAttribute: handleAttribute('has')
  301. };
  302. }
  303. });