decl.js.html 89 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>JSDoc: Source: decl.js</title>
  6. <script src="scripts/prettify/prettify.js"> </script>
  7. <script src="scripts/prettify/lang-css.js"> </script>
  8. <!--[if lt IE 9]>
  9. <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  10. <![endif]-->
  11. <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
  12. <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
  13. </head>
  14. <body>
  15. <div id="main">
  16. <h1 class="page-title">Source: decl.js</h1>
  17. <section>
  18. <article>
  19. <pre class="prettyprint source linenums"><code>// Licensed Materials - Property of IBM
  20. //
  21. // IBM Watson Analytics
  22. //
  23. // (C) Copyright IBM Corp. 2015, 2018
  24. //
  25. // US Government Users Restricted Rights - Use, duplication or
  26. // disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  27. ( function(
  28. Object,
  29. Array,
  30. String,
  31. Error,
  32. TypeError,
  33. ObjectPolyfill,
  34. Map,
  35. Set,
  36. Symbol,
  37. WeakMap
  38. )
  39. {
  40. "use strict";
  41. /*global console, setTimeout*/
  42. /*jshint latedef:false*/
  43. /**
  44. * Note: decl is not a constructor; call its static members.
  45. * @class module:barejs.decl
  46. * @abstract
  47. * @classdesc Module for declaring classes, interfaces, enums, and checking implementations.
  48. * decl uses the inheritance natively supported by JavaScript, instead of trying to emulate
  49. * multiple inheritance or providing "super" or "base" keyword emulation. This combined with the
  50. * fact that decl doesn't generate a constructor function (the defining code has to supply it)
  51. * leads to classes with no run-time overhead. It also means there is no "magic" performed.
  52. * Implementors using decl's inheritance are responsible for calling the base class
  53. * constructor and methods using the standard JavaScript call mechanism:
  54. *
  55. * function A( _name )
  56. * {
  57. * this.name = _name;
  58. * }
  59. *
  60. * decl.declareClass( A,
  61. * {
  62. * toString: function()
  63. * {
  64. * return this.name;
  65. * }
  66. * }
  67. *
  68. * function B( _name, _age )
  69. * {
  70. * // Invoke base class constructor on this object
  71. * A.call( this, _name );
  72. * this.age = _age;
  73. * }
  74. *
  75. * decl.declareClass( B, A,
  76. * {
  77. * toString: function()
  78. * {
  79. * // Invoke A::toString() and append our age to it.
  80. * return A.prototype.toString.call( this ) + "; age " + this.age;
  81. * }
  82. * } );
  83. *
  84. * In the debug version of barejs, decl adds a lot of metadata to provide a great debugging experience.
  85. * When defined with named constructors, objects created with decl will be highly discoverable and debuggable in browsers like Chrome.
  86. */
  87. // List of classes that are not allowed to be casted to.
  88. var uncastable_types = new Set();
  89. var uncastable_keys = new Map();
  90. // Grab slice from an array
  91. var slice = Array.prototype.slice;
  92. var hasOwnProperty = Object.prototype.hasOwnProperty;
  93. var canWriteFnName = !!( Object.defineProperties &amp;&amp; Object.getOwnPropertyDescriptor );
  94. /*istanbul ignore else: NodeJS supports this*/
  95. if ( canWriteFnName )
  96. {
  97. canWriteFnName = Object.getOwnPropertyDescriptor( Function.prototype, "name" );
  98. canWriteFnName = !canWriteFnName || canWriteFnName.configurable;
  99. }
  100. var reSymbolProto = /^(?:@@([a-zA-Z0-9_\$]+)|\[\[([a-zA-Z0-9_\$]+)\]\])$/;
  101. var reStaticIgnore = /^(?:constructor|prototype|name|interfaces|superclass|\$private)$/;
  102. var metaData = new WeakMap();
  103. /**
  104. * Convenience property to ease defining a read only property on an Interface.
  105. * It as simply a shortcut for '{ allowGet: true, allowSet: false }':
  106. *
  107. * decl.declareInterface( function MyInterface() {},
  108. * {
  109. * // The following two definitions have exactly the same effect:
  110. * myProperty: decl.readOnlyProperty,
  111. * myProperty: { allowGet: true, allowSet: false }
  112. * } );
  113. *
  114. * @member {object}
  115. * @readonly
  116. * @memberof module:barejs.decl
  117. */
  118. var readOnlyProperty = ObjectPolyfill.freeze( { allowGet: true, allowSet: false } );
  119. var native_ctors = [ Object, Array, Function, Boolean, Number, Math, Date, String, RegExp, Symbol,
  120. Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError ];
  121. // Detect a bunch more of optional native constructors, so our native_ctor array is complete
  122. //jshint -W117
  123. /*istanbul ignore else*/
  124. if ( typeof ArrayBuffer !== "undefined" )
  125. native_ctors.push( ArrayBuffer );
  126. /*istanbul ignore else*/
  127. if ( typeof Float32Array !== "undefined" )
  128. native_ctors.push( Float32Array );
  129. /*istanbul ignore else*/
  130. if ( typeof Float64Array !== "undefined" )
  131. native_ctors.push( Float64Array );
  132. /*istanbul ignore else*/
  133. if ( typeof Promise !== "undefined" )
  134. native_ctors.push( Promise );
  135. /*istanbul ignore else*/
  136. if ( typeof Proxy !== "undefined" )
  137. native_ctors.push( Proxy );
  138. /*istanbul ignore else*/
  139. if ( typeof Uint8Array !== "undefined" )
  140. native_ctors.push( Uint8Array );
  141. /*istanbul ignore else*/
  142. if ( typeof Uint8ClampedArray !== "undefined" )
  143. native_ctors.push( Uint8ClampedArray );
  144. /*istanbul ignore else*/
  145. if ( typeof Uint16Array !== "undefined" )
  146. native_ctors.push( Uint16Array );
  147. /*istanbul ignore else*/
  148. if ( typeof Uint32Array !== "undefined" )
  149. native_ctors.push( Uint32Array );
  150. //jshint +W117
  151. /**
  152. * Convenience property to ease defining a read/write property on an Interface.
  153. * It as simply a shortcut for '{ allowGet: true, allowSet: true }':
  154. *
  155. * decl.declareInterface( function MyInterface() {},
  156. * {
  157. * // The following two definitions have exactly the same effect:
  158. * myProperty: decl.readWriteProperty,
  159. * myProperty: { allowGet: true, allowSet: true }
  160. * } );
  161. *
  162. * @member {object}
  163. * @readonly
  164. * @memberof module:barejs.decl
  165. */
  166. var readWriteProperty = ObjectPolyfill.freeze( { allowGet: true, allowSet: true } );
  167. /*
  168. * Enable validating interfaces are implemented in debug mode
  169. * Except for Rhino, since it can't handle it...
  170. */
  171. /*istanbul ignore else: We always test in DEBUG*/
  172. if ( !__RELEASE__ &amp;&amp;
  173. // Detect Rhino so we can ignore it
  174. !( typeof load === "function" &amp;&amp; ( typeof Packages === "function" || typeof Packages === "object" ) )
  175. )
  176. {
  177. var validateQueue = [];
  178. /**
  179. * Validate a class implements all interface members.
  180. * @param {function} _class The class to validate.
  181. * @memberof module:barejs.decl~
  182. * @private
  183. */
  184. var validateInterfacesImplemented = function( _class )
  185. {
  186. var errors = [];
  187. if ( _class &amp;&amp; _class.interfaces )
  188. {
  189. _class.interfaces.forEach( function( _interface, _idxInterface )
  190. {
  191. var lines = [];
  192. for ( var members = InterfaceMetaData.get( _interface ).members, i = 0, len = members.length; i &lt; len; ++i )
  193. {
  194. var member = members[i];
  195. var error = null;
  196. switch ( member.type )
  197. {
  198. case "function":
  199. var def = member.interfaces[0].prototype[member.name];
  200. var impl = _class.prototype[member.name];
  201. if ( typeof impl !== "function" )
  202. error = "Missing implementation for {def}";
  203. else if ( ( impl.length !== def.length ) &amp;&amp; ( impl.proxy !== true ) )
  204. error = "Signature mismatch, {def} defines " + def.length + " arguments but implementation has " + impl.length;
  205. break;
  206. case "property":
  207. if ( !( member.name in _class.prototype ) )
  208. error = "Missing prototype definition for {def}";
  209. break;
  210. }
  211. if ( error !== null )
  212. lines.push( error.replace( "{def}", String( member ) ) );
  213. }
  214. if ( lines.length > 0 )
  215. {
  216. errors.push(
  217. "[" + describe( _interface ) + " @ index " + _idxInterface + "]\r\n\t\t" +
  218. lines.join( "\r\n\t\t" )
  219. );
  220. }
  221. }, this );
  222. }
  223. if ( errors.length > 0 )
  224. throw new Error( describe( _class ) + " has the following errors:\r\n\t" + errors.join( "\r\n\t" ) );
  225. };
  226. /**
  227. * Callback function to validate interfaces
  228. * @memberof module:barejs.decl~
  229. * @private
  230. */
  231. var handleValidateQueue = function()
  232. {
  233. // reset timeout id
  234. delete validateQueue.timeout;
  235. /*istanbul ignore if: sanity check, this function should not be called/queued with an empty queue*/
  236. if ( validateQueue.length &lt; 1 )
  237. return;
  238. // The code below will report errors by throwing an exception. Ensure the validateQueue is empty
  239. var queue = validateQueue;
  240. validateQueue = [];
  241. queue.forEach( function( _class )
  242. {
  243. // For interfaces, just create the metadata; this will do basic validation
  244. if ( isInterface( _class ) )
  245. InterfaceMetaData.get( _class );
  246. else
  247. validateInterfacesImplemented( _class );
  248. } );
  249. };
  250. }
  251. /**
  252. * Generic getter method
  253. * @param {string} _name The name of the property.
  254. * @returns The value of the property
  255. * @memberof module:barejs.decl~
  256. * @private
  257. */
  258. function _get( _name )
  259. {
  260. /*jshint validthis:true*/
  261. return this[_name];
  262. }
  263. /**
  264. * Generic setter method.
  265. * @param {string} _name The name of the property.
  266. * @param _value The value to assign.
  267. * @returns The new value of the property
  268. * @memberof module:barejs.decl~
  269. * @private
  270. */
  271. function _set( _name, _value )
  272. {
  273. /*jshint validthis:true*/
  274. return ( this[_name] = _value );
  275. }
  276. /**
  277. * Used during object prototype expansion to resolve a getter definition
  278. * @memberof module:barejs.decl~
  279. * @private
  280. */
  281. function resolvePropertyAccessor( _target )
  282. {
  283. // It is possible the target is also a property definition. Resolve it
  284. if ( _target &amp;&amp; ( typeof _target === "object" ) )
  285. {
  286. if ( hasOwnProperty.call( _target, "value" ) )
  287. return _target.value;
  288. else if ( hasOwnProperty.call( _target, "get" ) )
  289. return _target.get;
  290. }
  291. return _target;
  292. }
  293. /**
  294. * Iterator function that will modify the context to have a defineProperty
  295. * definition instead of direct properties. If the value is already a property definition, it is left
  296. * untouched. Once the object has been parsed, it can then be given to Object.create.
  297. * @memberof module:barejs.decl~
  298. * @private
  299. */
  300. function toDefineProperty( _value, _name, _object, _lookup )
  301. {
  302. var def;
  303. if ( _value &amp;&amp; ( typeof _value === "object" ) )
  304. {
  305. if ( hasOwnProperty.call( _value, "value" ) )
  306. {
  307. def = _value;
  308. }
  309. // Guard against map values as they have
  310. else if ( hasOwnProperty.call( _value, "get" ) || hasOwnProperty.call( _value, "set" ) )
  311. {
  312. def = _value;
  313. // If there is no property support, we silently ignore properties
  314. /*istanbul ignore if: NodeJS supports properties*/
  315. if ( !ObjectPolyfill.propertyGetSetSupport )
  316. return null;
  317. // If there is a getter or setter, see if we need to resolve it
  318. if ( typeof def.get === "string" )
  319. {
  320. def.getterName = def.get;
  321. def.get = resolvePropertyAccessor( _object[def.get] || ( _lookup &amp;&amp; _lookup[def.get] ) );
  322. }
  323. if ( typeof def.set === "string" )
  324. {
  325. def.setterName = def.set;
  326. def.set = resolvePropertyAccessor( _object[def.set] || ( _lookup &amp;&amp; _lookup[def.set] ) );
  327. }
  328. }
  329. }
  330. if ( !def )
  331. {
  332. def =
  333. {
  334. // Make Symbols and string keys starting with "_" not enumerable by default
  335. enumerable: ObjectPolyfill.shouldBeEnumerable( _name ),
  336. writable: true,
  337. value: _value
  338. };
  339. }
  340. return def;
  341. }
  342. /**
  343. * Same purpose as toDefineProperty, but specialised for interfaces, on which we expect only
  344. * functions or objects that define get/set access. Performs validation no other properties are present.
  345. * @memberof module:barejs.decl~
  346. * @private
  347. */
  348. function toDefinePropertyInterface( _value, _name )
  349. {
  350. var ok = false;
  351. var allowGet, allowSet;
  352. switch ( _value &amp;&amp; typeof _value )
  353. {
  354. case "function":
  355. // Functions are always OK
  356. ok = true;
  357. break;
  358. case "object":
  359. // If the decl constants where used, there is no sense in validating them
  360. ok = ( _value === readOnlyProperty ) || ( _value === readWriteProperty );
  361. // If not, validate the object given to us
  362. if ( !ok )
  363. {
  364. allowGet = ( "allowGet" in _value ) &amp;&amp; _value.allowGet;
  365. allowSet = ( "allowSet" in _value ) &amp;&amp; _value.allowSet;
  366. // allowGet, if defined, should be a boolean
  367. if ( typeof allowGet !== "boolean" )
  368. throw new TypeError( "allowGet value is not a boolean" );
  369. // allowSet, if defined, should be a boolean
  370. if ( typeof allowSet !== "boolean" )
  371. throw new TypeError( "allowSet value is not a boolean" );
  372. ok = allowGet || allowSet; // at least one needs to be true.
  373. }
  374. break;
  375. }
  376. if ( !ok )
  377. throw new TypeError( "Values on an interface prototype must be either a function or an object containing allowGet or allowSet boolean properties." );
  378. return { enumerable: true, value: _value };
  379. }
  380. /**
  381. * Convenience method that will add a displayName to _function if not present, by concatenating
  382. * _objectName and _propName with a '.', optionally appending _suffix after _propName.
  383. * @param {function} _function The function to add the displayName to.
  384. * @param {string} _objectName The name of the object, for example "MyClass.prototype"
  385. * @param {string} _propName The name of the property (the function is added as), for example "myMethod"
  386. * @param {string} [_suffix] Optional: part to append to the name, for example " [GET]" for a getter function
  387. * @memberof module:barejs.decl~
  388. * @private
  389. */
  390. function displayName( _function, _objectName, _propName, _suffix )
  391. {
  392. if ( canWriteFnName &amp;&amp; !hasOwnProperty.call( _function, "name" ) )
  393. ObjectPolyfill.defineProperty( _function, "name", { configurable: true, value: _propName } );
  394. if ( !( "displayName" in _function ) )
  395. ObjectPolyfill.defineProperty( _function, "displayName", { configurable: true, value: _objectName + "." + _propName + ( _suffix || "" ) } );
  396. }
  397. /**
  398. * Utility method that returns _def expanded to a defineProperties argument.
  399. * Arguments that are a property definition are left alone, other are expanded to be a property definition
  400. * @param {object} _def The object whose properties to expand.
  401. * @param {function} _callback Function to use for the expand operation.
  402. * @param {string} [_objName] Optional: the logical name of _def, e.g. "MyClass.prototype"
  403. * @returns {object} _def made suitable for Object.defineProperties (or second argument of Object.create).
  404. * @memberof module:barejs.decl~
  405. * @private
  406. */
  407. function expandDefineProperties( _def, _lookup, _callback, _objName )
  408. {
  409. if ( _def )
  410. {
  411. // Ensure object
  412. _def = Object( _def );
  413. for ( var names = Object.keys( _def ), i = 0, len = names.length, name, prop, sym; i &lt; len; ++i )
  414. {
  415. name = names[i];
  416. sym = reSymbolProto.exec( name );
  417. if ( sym )
  418. {
  419. //jshint -W122
  420. // The regexp matches one of two possible forms ("@@symbolName" or "[[symbolName]]"),
  421. // which means the symbol name may be in either capture group
  422. sym = Symbol[ sym[1] || sym[2] ];
  423. if ( typeof sym !== "symbol" )
  424. {
  425. delete _def[name];
  426. continue;
  427. }
  428. //jshint +W122
  429. }
  430. prop = _callback( _def[name], sym || name, _def, _lookup );
  431. if ( sym )
  432. {
  433. delete _def[name];
  434. _def[sym] = prop;
  435. }
  436. else if ( _def[name] !== prop )
  437. {
  438. // on rare occasions we may need to drop a property, e.g. when there is no getter/setter support.
  439. if ( prop === null )
  440. delete _def[name];
  441. else
  442. _def[name] = prop;
  443. }
  444. if ( prop &amp;&amp; _objName )
  445. {
  446. if ( "value" in prop )
  447. {
  448. if ( ObjectPolyfill.isCallable( prop.value ) )
  449. displayName( prop.value, _objName, name );
  450. }
  451. else
  452. {
  453. if ( prop.get &amp;&amp; !prop.getterName )
  454. displayName( prop.get, _objName, name, " [GET]" );
  455. if ( prop.set &amp;&amp; !prop.setterName )
  456. displayName( prop.set, _objName, name, " [SET]" );
  457. }
  458. }
  459. }
  460. }
  461. return _def;
  462. }
  463. /**
  464. * Tells the proxy system casting to this type is not allowed. Should only be used for very low
  465. * level classes, otherwise performance will be impacted.
  466. * @param {function} _class The type to disallow casting to
  467. * @returns {Symbol} Key to cast to this type. NEVER export this key in any way.
  468. * @memberof module:barejs.decl
  469. */
  470. function preventCast( _class ){
  471. if ( typeof _class !== "function" )
  472. throw new TypeError( "_class must be a function" );
  473. if ( uncastable_types.has( _class ) )
  474. throw new Error( "Already declared uncastable" );
  475. // Register uncastable, generate key and store it at the correct index
  476. // Symbol is meant to make a "private key", so it seems suitable enough to use as a key for preventCast.
  477. var key = Symbol( _class.name );
  478. uncastable_types.add( _class );
  479. uncastable_keys.set( key, _class );
  480. return key;
  481. }
  482. // Allow decl to cast back without knowing the right type
  483. // NOTE: This value should never be exported
  484. var ObjectKey = preventCast( Object );
  485. /**
  486. * Method that sets up inheritance. Doesn't perform any validation, this should be done beforehand.
  487. * @param {function} _class Class to set up the inheritance for
  488. * @param {function} _base The class to derive from.
  489. * @returns {function} Class, now deriving from _base
  490. * @memberof module:barejs.decl~
  491. * @private
  492. */
  493. function derive( _class, _base, _proto )
  494. {
  495. // Apply prototype inheritance
  496. _class.prototype = Object.create( _base.prototype, _proto || undefined );
  497. // Reset the constructor (non enumerable, but writable, just like browsers set it).
  498. ObjectPolyfill.defineProperty( _class.prototype, "constructor", { writable : true, value : _class } );
  499. // Set superclass on constructor function
  500. // Note: decl methods should not rely too much on superclass being set, it's for convenience only
  501. return ObjectPolyfill.defineProperty( _class, "superclass", { value : _base } );
  502. }
  503. //
  504. // Helper classes for metadata
  505. //
  506. /**
  507. * @classdesc Base class to gather information about a member of an interface.
  508. * @class module:barejs.decl~InterfaceMember
  509. * @param {function[]} _interfaces The interface(s) on which the method is defined.
  510. * @param {string} _name The name of the method on the interface.
  511. * @private
  512. */
  513. function InterfaceMember( _interfaces, _name )
  514. {
  515. this.interfaces = _interfaces;
  516. this.name = _name;
  517. }
  518. derive( InterfaceMember, Object,
  519. /** @lends module:barejs.decl~InterfaceMember */
  520. {
  521. type: { value: "member" },
  522. /**
  523. * Provides a string representation of the member, for example "member myFunction on interface IMyInterface".
  524. * @returns {string} The string representation of the member
  525. */
  526. toString: { value: function toString()
  527. {
  528. return this.type + " \"" + String( this.name ) + "\" defined on " + this.interfaces.map( describe ).join( ", " );
  529. } }
  530. } );
  531. /**
  532. * @classdesc Stores information about a method on an interface.
  533. * @class module:barejs.decl~InterfaceMethod
  534. * @param {function} _interface The interface on which the method is defined.
  535. * @param {string} _name The name of the method on the interface.
  536. * @private
  537. */
  538. function InterfaceMethod( _interface, _name )
  539. {
  540. InterfaceMember.call( this, [_interface], _name );
  541. }
  542. derive( InterfaceMethod, InterfaceMember,
  543. /** @lends module:barejs.decl~InterfaceMethod */
  544. {
  545. type: { value: "function" }
  546. } );
  547. /**
  548. * @classdesc Stores information about a property on an interface
  549. * @class module:barejs.decl~InterfaceProperty
  550. * @param {function[]} _interfaces The interfaces on which the property is defined.
  551. * @param {string} _name The name of the property on the interface.
  552. * @param {boolean} _allowGet Whether getting this property is allowed.
  553. * @param {boolean} _allowSet Whether setting this property is allowed.
  554. * @private
  555. */
  556. function InterfaceProperty( _interfaces, _name, _allowGet, _allowSet )
  557. {
  558. InterfaceMember.call( this, _interfaces, _name );
  559. this.allowGet = _allowGet;
  560. this.allowSet = _allowSet;
  561. }
  562. derive( InterfaceProperty, InterfaceMember,
  563. /** @lends module:barejs.decl~InterfaceProperty */
  564. {
  565. type: { value: "property" },
  566. /**
  567. * Merge two definitions into a new InterfaceProperty. Used to resolve collisions between interfaces
  568. * @param {module:barejs.decl~InterfaceProperty} _otherProperty The property to merge with.
  569. * @returns {module:barejs.decl~InterfaceProperty} The merged InterfaceProperty. Might be the original, if the interfaces was already known.
  570. */
  571. merge: { value: function( _otherProperty )
  572. {
  573. if ( _otherProperty === this ) // sanity
  574. return this;
  575. // merge interfaces arrays
  576. for ( var interfaces = this.interfaces.slice( 0 ), i = 0, len = _otherProperty.interfaces.length, iface; i &lt; len; ++i )
  577. {
  578. if ( interfaces.indexOf( iface = _otherProperty.interfaces[i] ) )
  579. interfaces.push( iface );
  580. }
  581. return new InterfaceProperty( interfaces, this.name, this.allowGet || _otherProperty.allowGet, this.allowSet || _otherProperty.allowSet );
  582. } }
  583. } );
  584. /**
  585. * Stores metadata about members of this interface, members of base interfaces and the combined list.
  586. * This allows methods like hasInterface and proxy to quickly iterate over the list of members.
  587. * @class module:barejs.decl~InterfaceMetaData
  588. * @memberof module:barejs.decl~
  589. * @private
  590. */
  591. function InterfaceMetaData()
  592. {
  593. this.directMembers = [];
  594. this.inheritedMembers = [];
  595. this.members = null;
  596. }
  597. /**
  598. * merge directMembers and inheritedMembers into one
  599. */
  600. InterfaceMetaData.prototype.merge = function()
  601. {
  602. var mergeMap;
  603. var i, len, member;
  604. if ( this.members )
  605. return;
  606. if ( this.inheritedMembers.length &lt; 1 )
  607. {
  608. this.members = this.directMembers;
  609. }
  610. else if ( this.directMembers.length &lt; 1 )
  611. {
  612. this.members = this.inheritedMembers;
  613. }
  614. else
  615. {
  616. mergeMap = Object.create( null );
  617. // Start by copying the directMembers
  618. this.members = this.directMembers.slice( 0 );
  619. // Then iterate them to initialize the merge map
  620. for ( i = 0, len = this.members.length; i &lt; len; ++i )
  621. mergeMap[this.members[i].name] = true;
  622. // Next, iterate inherited members
  623. for ( i = 0, len = this.inheritedMembers.length; i &lt; len; ++i )
  624. {
  625. // No point in updating the merge map, this is the last iteration of the merge
  626. // Only add the member if it wasn't redefined
  627. if ( mergeMap[(member = this.inheritedMembers[i]).name] !== true )
  628. this.members.push( member );
  629. }
  630. this.members.sort( function( _a, _b )
  631. {
  632. if ( _a.name === _b.name )
  633. return 0;
  634. var ta = typeof _a.name;
  635. return ( ta === typeof _b.name ) &amp;&amp; ( ta === "string" ) &amp;&amp; ( _a.name > _b.name ) ? 1 : -1;
  636. } );
  637. }
  638. };
  639. /**
  640. * Build meta data for an interface, like the list of all methods required by the interface.
  641. */
  642. InterfaceMetaData.get = function( _interface )
  643. {
  644. if ( !isInterface( _interface ) )
  645. throw new TypeError( "_interface is not an Interface" );
  646. var meta = metaData.get( _interface );
  647. if ( !meta )
  648. {
  649. metaData.set( _interface, meta = new InterfaceMetaData() );
  650. var mergeMap = Object.create( null );
  651. // Merge inherited members
  652. if ( _interface.interfaces )
  653. {
  654. _interface.interfaces.forEach( function( _extendedInterface )
  655. {
  656. var members = InterfaceMetaData.get( _extendedInterface ).members, member, existing;
  657. for ( var i = 0, len = members.length; i &lt; len; ++i )
  658. {
  659. member = members[i];
  660. if ( ( existing = mergeMap[member.name] ) &amp;&amp; ( existing !== member ) )
  661. {
  662. // See if we need to do property merge magic
  663. if ( ( existing.type === "property" ) &amp;&amp; ( member.type === "property" ) )
  664. {
  665. meta.inheritedMembers[meta.inheritedMembers.indexOf( existing )] =
  666. mergeMap[member.name] = existing.merge( member );
  667. }
  668. else
  669. {
  670. // Report the conflict
  671. throw new Error( describe( _interface ) + " has a conflict in extended interfaces: The " + existing + " conflicts with " + member + "." );
  672. }
  673. }
  674. else
  675. {
  676. mergeMap[member.name] = member;
  677. meta.inheritedMembers.push( member );
  678. }
  679. }
  680. } );
  681. }
  682. // Add direct members (freeze prototype too, to protect it from modification after metadata is built).
  683. for ( var names = Object.keys( ObjectPolyfill.freeze( _interface.prototype ) ).concat( ObjectPolyfill.getOwnPropertySymbols( _interface.prototype ) ), i = 0, len = names.length; i &lt; len; ++i )
  684. {
  685. var name = names[i], target = _interface.prototype[name];
  686. // Protect proxy features from colliding with the interface.
  687. if ( ( name === "as" ) || ( name === "is" ) )
  688. throw new Error( "The " + ( new InterfaceMember( [_interface], name ) ) + " uses the reserved name \"" + name + "\", which is not allowed." );
  689. // Explicitly ignore the constructor property for Rhino
  690. if ( name === "constructor" )
  691. continue;
  692. var member = null, existing = mergeMap[name];
  693. // First, just create the metadata (not adding it)
  694. switch ( target &amp;&amp; typeof target )
  695. {
  696. case "function":
  697. member = new InterfaceMethod( _interface, name );
  698. break;
  699. case "object":
  700. if ( ( "allowGet" in target ) || ( "allowSet" in target ) )
  701. {
  702. member = new InterfaceProperty( [_interface], name, target.allowGet === true, target.allowSet === true );
  703. if ( !( member.allowGet || member.allowSet ) )
  704. throw new Error( "The " + member + " is invalid: it doesn't allow get or set." );
  705. }
  706. break;
  707. }
  708. if ( !member )
  709. {
  710. throw new Error(
  711. "The " + ( new InterfaceMember( [_interface], name ) ) + " is invalid: expected a function, " +
  712. "or an object with allowGet or allowSet property, but got " + ( typeof target ) + target + " instead."
  713. );
  714. }
  715. // Override or report conflict
  716. if ( existing )
  717. {
  718. // Interfaces are allowed to redefine properties with more access (i.e. readWrite over read).
  719. // They are not allowed to revoke access or redefine a property with similar access
  720. if ( ( member.type === "property" ) &amp;&amp; ( existing.type === "property" ) )
  721. {
  722. // Test if the interface is removing access
  723. if ( existing.allowGet &amp;&amp; !member.allowGet )
  724. throw new Error( "The " + member + " has a conflict with " + existing + ": it is removing get access." );
  725. if ( existing.allowSet &amp;&amp; !member.allowSet )
  726. throw new Error( "The " + member + " has a conflict with " + existing + ": it is removing set access." );
  727. if ( ( existing.allowGet === member.allowGet ) &amp;&amp; ( existing.allowSet === member.allowSet ) )
  728. throw new Error( "The " + member + " is redefining " + existing + " with equal get/set access (so it is obsolete)." );
  729. }
  730. else
  731. {
  732. throw new Error( "The " + member + " conflicts with " + existing + "." );
  733. }
  734. }
  735. meta.directMembers.push( member );
  736. }
  737. meta.merge();
  738. }
  739. return meta;
  740. };
  741. /**
  742. * Stores metadata about names and values on an enum.
  743. * This allows for quick reverse lookup
  744. * @class module:barejs.decl~EnumMetaData
  745. * @private
  746. */
  747. function EnumMetaData( _enum )
  748. {
  749. this.names = ObjectPolyfill.freeze( Object.keys( _enum ) );
  750. // Use _get to resolve the enum property value
  751. this.values = ObjectPolyfill.freeze( this.names.map( _get, _enum ) );
  752. }
  753. /**
  754. * Perform a case insensitive name lookup for an enum
  755. * @param {module:barejs.decl~Enum} _enum The enum to look up the name for
  756. * @param {string} _name The name to match
  757. * @returns {string} the found name, or null if not found.
  758. */
  759. EnumMetaData.prototype.ciName = function( _name )
  760. {
  761. var nameLower = String( _name ).toLowerCase();
  762. for ( var i = this.names.length - 1; i >= 0; --i )
  763. {
  764. if ( nameLower === this.names[i].toLowerCase() )
  765. return this.names[i];
  766. }
  767. return null;
  768. };
  769. /**
  770. * Retrieve metadata for an enum
  771. */
  772. EnumMetaData.get = function( _enum )
  773. {
  774. var meta = metaData.get( _enum.constructor );
  775. if ( !meta )
  776. metaData.set( _enum.constructor, meta = new EnumMetaData( _enum ) );
  777. return meta;
  778. };
  779. //
  780. // Helper classes
  781. //
  782. function NullObject() {}
  783. NullObject.prototype = null;
  784. /**
  785. * Internal class for decl, serving as a base class for {@link module:barejs.decl~Interface Interface} and {@link module:barejs.decl~Enum Enum}.
  786. * Does not have Object.prototype in its prototype chain, so objects deriving from SpecialType are not instanceof Object.
  787. * @class module:barejs.decl~SpecialType
  788. */
  789. function SpecialType() {}
  790. derive( SpecialType, NullObject,
  791. /** @lends module:barejs.decl~SpecialType# */
  792. {
  793. // JSHint complains hasOwnProperty is a really bad name, but we are simply "restoring" it on a special type.
  794. // jshint -W001
  795. /**
  796. * A SpecialType is not instanceof Object, but does have the Object.prototype.hasOwnProperty method.
  797. * @function
  798. * @param {string} _name The name of the property to check
  799. * @returns {boolean} True if the object has a direct property with _name, false otherwise.
  800. */
  801. hasOwnProperty: { value: hasOwnProperty }
  802. // jshint +W001
  803. } );
  804. /**
  805. * Base class for interfaces
  806. * @class module:barejs.decl~Interface
  807. * @extends module:barejs.decl~SpecialType
  808. */
  809. function Interface() {}
  810. // Make Interface a "special type" (new Interface() instanceof Object === false)
  811. derive( Interface, SpecialType,
  812. /** @lends module:barejs.decl~Interface# */
  813. {
  814. /**
  815. * toString for Interface.
  816. * @function
  817. * @returns {string} The string [interface InterfaceName], where InterfaceName is
  818. * the name of the interface constructor function, or "Interface" for anonymous interfaces.
  819. */
  820. toString: { value: function toString()
  821. {
  822. return "[interface " + ( this.constructor.name || "Interface" ) + "]";
  823. } }
  824. } );
  825. /**
  826. * Base class for Enum types declared with {@link module:barejs.decl#declareEnum decl.declareEnum}
  827. * @class module:barejs.decl~Enum
  828. * @extends module:barejs.decl~SpecialType
  829. */
  830. function Enum() {}
  831. // Make Enum a "special type" (new Enum() instanceof Object === false)
  832. derive( Enum, SpecialType,
  833. /** @lends module:barejs.decl~Enum# */
  834. {
  835. // Allow subclasses to redefine these methods (so make them writable)
  836. /**
  837. * Enum method: get the name of the specified value. If multiple names lead to the same value, the
  838. * first found name is returned
  839. * @function
  840. * @param {string} _value The value for which to get the name
  841. * @returns The name of the value, or null if not found
  842. */
  843. nameOf:
  844. {
  845. writable: true,
  846. value: function nameOf( _value )
  847. {
  848. var meta = EnumMetaData.get( this );
  849. return meta.names[meta.values.indexOf( _value )] || null;
  850. }
  851. },
  852. /**
  853. * Enum method: get the value of the specified name
  854. * @function
  855. * @param {string} _name The name of the value to get
  856. * @param {boolean} [_caseInsensitive=false] Optional, set to true to perform a case insensitive search
  857. * @returns {object} The enum value, or null if it wasn't found.
  858. */
  859. valueOf:
  860. {
  861. writable: true,
  862. value: function valueOf( _name, _caseInsensitive )
  863. {
  864. // Case sensitive or insensitive, see if the name is defined.
  865. if ( this.hasOwnProperty( _name ) )
  866. return this[_name];
  867. // If we're not case insensitive, we're not going to find the value
  868. if ( _caseInsensitive !== true )
  869. return null;
  870. // Do a case insensitive lookup
  871. _name = EnumMetaData.get( this ).ciName( _name );
  872. // ciName will return null if the name didn't match any entry
  873. return _name &amp;&amp; this[_name];
  874. }
  875. },
  876. /**
  877. * Enum method: check if the enum has the specified name
  878. * @function
  879. * @param {string} _name The name to check
  880. * @param {boolean} [_caseInsensitive=false] Optional, set to true to perform a case insensitive search
  881. * @returns {boolean} True if the enum has the name, false otherwise.
  882. */
  883. hasName:
  884. {
  885. writable: true,
  886. value: function hasName( _name, _caseInsensitive )
  887. {
  888. // Always check hasOwnProperty first, to avoid the array lookup of case insensitive.
  889. return this.hasOwnProperty( _name ) || ( ( _caseInsensitive === true ) &amp;&amp; ( EnumMetaData.get( this ).ciName( _name ) !== null ) );
  890. }
  891. },
  892. /**
  893. * Check if the enum has the specified value
  894. * @function
  895. * @param {object} _value The enum value to check for
  896. * @returns {boolean} True if the enum has the value, false otherwise.
  897. */
  898. hasValue:
  899. {
  900. writable: true,
  901. value: function hasValue( _value )
  902. {
  903. return EnumMetaData.get( this ).values.indexOf( _value ) >= 0;
  904. }
  905. },
  906. /**
  907. * Utility method to parse an enum value, for example from input parsed from JSON.
  908. * Takes the following steps:
  909. *
  910. * 1. if _value is an actual enum value, return _value.
  911. * 2. if _value is a name of enum, return the value of that name
  912. * 3. if _throw is true, throw a RangeError
  913. * 4. return null
  914. *
  915. * @function
  916. * @param _value The value to parse
  917. * @param {boolean} _caseInsensitive Whether name matching should be case insensitive.
  918. * Defaults to false, specify true for case insensitive.
  919. * @param {boolean} [_throw=false] Optional: set to true to perform strict validation.
  920. * @returns The parsed value, or null if the value didn't parse.
  921. */
  922. parse:
  923. {
  924. writable: true,
  925. value: function parse( _value, _caseInsensitive, _throw )
  926. {
  927. var enumMeta = EnumMetaData.get( this );
  928. if ( enumMeta.values.indexOf( _value ) >= 0 )
  929. return _value;
  930. // After this point, _value is considered to be a potential name
  931. var name = _value;
  932. // Perform case insensitive lookup if needed
  933. if ( !this.hasOwnProperty( name ) &amp;&amp; ( _caseInsensitive === true ) )
  934. name = EnumMetaData.get( this ).ciName( name );
  935. if ( name &amp;&amp; this.hasOwnProperty( name ) )
  936. return this[name];
  937. if ( _throw === true )
  938. throw new RangeError( "Could not parse enum value " + _value );
  939. return null;
  940. }
  941. },
  942. /**
  943. * Get the names of an enum
  944. * @function
  945. * @returns {Array} The names of an enum
  946. */
  947. names:
  948. {
  949. writable: true,
  950. value: function names()
  951. {
  952. return EnumMetaData.get( this ).names;
  953. }
  954. },
  955. /**
  956. * Get the values of an enum
  957. * @function
  958. * @returns {Array} The values of an enum
  959. */
  960. values:
  961. {
  962. writable: true,
  963. value: function values()
  964. {
  965. return EnumMetaData.get( this ).values;
  966. }
  967. },
  968. /**
  969. * Iterate over all enum name/value pairs. Signature attempts to match Array.prototype.forEach.
  970. * @function
  971. * @param {function} _callback The callback, called with _value, _name, _enum
  972. * @param {object} [_thisArg] Optional: the scope to call the callback in.
  973. */
  974. forEach:
  975. {
  976. writable: true,
  977. value: function forEach( _callback/*, _thisArg*/ )
  978. {
  979. for ( var meta = EnumMetaData.get( this ), idx = 0, len = meta.names.length, thisArg = ( arguments[1] || null ) &amp;&amp; Object( arguments[1] ); idx &lt; len; ++idx )
  980. _callback.call( thisArg, meta.values[idx], meta.names[idx], this );
  981. }
  982. },
  983. /**
  984. * toString for Enum.
  985. * @function
  986. * @returns {string} The string [enum EnumName], where EnumName is
  987. * the name of the enum constructor function, or "Enum" for anonymous enumerations.
  988. */
  989. toString: { value: function toString()
  990. {
  991. return "[enum " + ( this.constructor.name || "Enum" ) + "]";
  992. } }
  993. } );
  994. // These methods are added to a declareClass prototype
  995. var classProtoExtension =
  996. {
  997. /**
  998. * To be executed in the context of an object.
  999. * Allow "casting" between interface proxies and the original object.
  1000. * @param {function} _class The type (constructor function) to cast to.
  1001. * @param {boolean} [_strict=false] Optional: set to true to avoid a duck-type check, and only check `decl`
  1002. * metadata for interfaces implemented.
  1003. * @returns {object} A proxy, the original object, or null if the "cast" could not be performed.
  1004. */
  1005. as: { value : function as( _class, _strict )
  1006. {
  1007. // jshint validthis:true
  1008. // Give ObjectKey special treatment, making it a "master key", allowing to cast back objects
  1009. // that are not even instanceof Object.
  1010. if ( _class === ObjectKey )
  1011. return this;
  1012. // _class is supposed to be either a constructor function or a proxy key
  1013. if ( typeof _class !== "function" )
  1014. {
  1015. // Allow casting back by key (Symbol, handed out by preventCast).
  1016. var type = uncastable_keys.get( _class );
  1017. if ( type )
  1018. return this instanceof type ? this : null;
  1019. throw new TypeError( "as requires _class to be a (constructor) function" );
  1020. }
  1021. else if ( uncastable_types.has( _class ) )
  1022. {
  1023. throw new Error( "as does not allow casting to this type, specify a more specific type" );
  1024. }
  1025. // If we get here, _class is a function
  1026. if ( isInterface( _class ) &amp;&amp; hasInterface( this, _class, _strict ) )
  1027. return proxy( this, _class );
  1028. else if ( this instanceof _class )
  1029. return this;
  1030. // Type change failed
  1031. return null;
  1032. } },
  1033. /**
  1034. * To be executed in the context of an object.
  1035. * Allow checking if an object adheres to a specific type, or equals an instance.
  1036. * @param {function} _other (Type|Interface) to test for, OR {object} (Instance) to check equality against
  1037. * @param {boolean} [_strict=false] Optional: if _other is an Interface, set to true to avoid a duck-type
  1038. * check, and only check `decl` metadata for interfaces implemented.
  1039. * If `_other` is not an Interface, this param is ignored.
  1040. * @returns {boolean} True if this object adheres to the type, or equals the _other instance. False otherwise.
  1041. */
  1042. is: { value: function is( _other, _strict )
  1043. {
  1044. // jshint validthis:true
  1045. if ( typeof _other === "function" )
  1046. return ( this instanceof _other ) || ( ( isInterface( _other ) &amp;&amp; hasInterface( this, _other, _strict ) ) );
  1047. else if ( isProxy( _other ) )
  1048. return this === _other.as( ObjectKey );
  1049. else
  1050. return this === _other;
  1051. } }
  1052. };
  1053. //
  1054. // Helper methods
  1055. //
  1056. /**
  1057. * Attempts to discover an object's base class.
  1058. * @param {function} _class Class constructor function
  1059. * @returns {function} The bas constructor, defaults to Object if the base cannot be determined.
  1060. * @memberof module:barejs.decl~
  1061. * @private
  1062. */
  1063. function getBase( _class )
  1064. {
  1065. var proto = null;
  1066. if ( typeof _class === "function" )
  1067. {
  1068. proto = ObjectPolyfill.getPrototypeOf( _class.prototype );
  1069. proto = proto &amp;&amp; proto.constructor ? proto.constructor : Object;
  1070. }
  1071. return proto;
  1072. }
  1073. /**
  1074. * Get the type of _target.
  1075. * This is typeof enhanced.
  1076. * @param _target The thing to get the type of.
  1077. * @memberof module:barejs.decl~
  1078. * @private
  1079. */
  1080. function type( _target )
  1081. {
  1082. var t = _target === null ? "null" : typeof _target;
  1083. if ( t === "function" )
  1084. {
  1085. if ( _target.prototype instanceof Interface )
  1086. t = "interface";
  1087. else if ( _target.prototype instanceof Enum )
  1088. t = "enum";
  1089. else if ( !( "prototype" in _target ) )
  1090. t = "native function";
  1091. else if ( "superclass" in _target )
  1092. t = "class";
  1093. }
  1094. else if ( t === "object" )
  1095. {
  1096. if ( Array.isArray( _target ) )
  1097. return "array";
  1098. else if ( _target instanceof Enum )
  1099. t = "enum";
  1100. else if ( _target instanceof Interface )
  1101. t = "proxy";
  1102. }
  1103. return t;
  1104. }
  1105. /**
  1106. * Helper method that tries to get the name of a class.
  1107. * @param {function|Object} _target The object to get the name of
  1108. * @returns {string} The name of target, or null
  1109. * @memberof module:barejs.decl~
  1110. * @private
  1111. */
  1112. function name( _target )
  1113. {
  1114. if ( !_target )
  1115. return null;
  1116. else if ( typeof _target === "function" )
  1117. return _target.name || null;
  1118. else if ( _target.constructor )
  1119. return _target.constructor.name || null;
  1120. return null;
  1121. }
  1122. /**
  1123. * Helper method that tries to build a description of an object. It gets the right type description
  1124. * using type, and tries to append the name to it. The name is only available in environments that
  1125. * support the name property on functions, and of course the function must be named.
  1126. * Example: describe( SampleClass );
  1127. * If SampleClass is a named function this might return "class SampleClass".
  1128. * Otherwise, it will return "class (Anonymous)".
  1129. * @param {function} _fn The function to describe.
  1130. * @memberof module:barejs.decl~
  1131. * @private
  1132. */
  1133. function describe( _target )
  1134. {
  1135. var n = name( _target );
  1136. return n ? type( _target ) + " " + n : type( _target );
  1137. }
  1138. /**
  1139. * Check if the prototype object is clean (has no properties defined).
  1140. * @param {function} _class The constructor function whose prototype object to check.
  1141. * @param {string} [_requester] Optional: name of the function requesting the check
  1142. * @memberof module:barejs.decl~
  1143. * @private
  1144. */
  1145. function checkCleanPrototype( _class, _requester )
  1146. {
  1147. var props = Object.keys( _class.prototype ),
  1148. idx = props.indexOf( "constructor" );
  1149. if ( idx >= 0 )
  1150. props.splice( idx, 1 );
  1151. if ( props.length > 0 )
  1152. {
  1153. throw new Error(
  1154. ( _requester ? _requester + ": " : "" ) + describe( _class ) + " already has properties defined on the prototype: " +
  1155. props.join( ", " )
  1156. );
  1157. }
  1158. }
  1159. /**
  1160. * Port "static" functions over from the base class.
  1161. * @param {function} _class The constructor function to update with base class static functions.
  1162. * @param {function} _base The base constructor to copy static functions of.
  1163. * @memberof module:barejs.decl~
  1164. * @private
  1165. */
  1166. function applyStatic( _class, _base, _stat )
  1167. {
  1168. if ( _base &amp;&amp; ( native_ctors.indexOf( _base ) &lt; 0 ) )
  1169. {
  1170. var descriptors = ObjectPolyfill.getOwnPropertyDescriptors( _base );
  1171. var staticInherited;
  1172. var keys = Object.keys( descriptors );
  1173. for ( var i = 0, len = keys.length; i &lt; len; ++i )
  1174. {
  1175. var key = keys[i];
  1176. // Ignore keys that match reStaticIgnore,
  1177. if ( ( typeof key !== "string" || !reStaticIgnore.test( key ) ) &amp;&amp;
  1178. // or keys that are already on _class or _stat,
  1179. ( !hasOwnProperty.call( _class, key ) &amp;&amp; !( _stat &amp;&amp; hasOwnProperty.call( _stat, key ) ) ) &amp;&amp;
  1180. // or keys that are explicitly ignored using a $private function
  1181. !( _base.$private &amp;&amp; _base.$private( key ) ) )
  1182. {
  1183. var def = descriptors[key];
  1184. if ( "value" in def &amp;&amp; typeof def.value !== "function" &amp;&amp; def.writable &amp;&amp; ObjectPolyfill.propertyGetSetSupport )
  1185. {
  1186. // Upgrade def to a get/set proxy
  1187. def =
  1188. {
  1189. configurable: def.configurable,
  1190. enumerable: def.enumerable,
  1191. "get": _get.bind( _base, key ),
  1192. "set": _set.bind( _base, key )
  1193. };
  1194. }
  1195. // Make sure the definition is always configurable, as we're "inheriting"
  1196. def.configurable = true;
  1197. if ( !staticInherited )
  1198. staticInherited = {};
  1199. staticInherited[key] = def;
  1200. }
  1201. }
  1202. if ( staticInherited )
  1203. defineObject( _class, staticInherited );
  1204. }
  1205. if ( _stat )
  1206. defineObject( _class, _stat );
  1207. }
  1208. /**
  1209. * Helper function that sets the interfaces for a class and validates they are actually interfaces.
  1210. * This method does not perform any validation on _class or its state.
  1211. * @param {function} _class The class constructor
  1212. * @param {Array} _interfaces The list of interfaces.
  1213. * @param {Array} _baseInterfaces The list of interfaces on the base class.
  1214. * @memberof module:barejs.decl~
  1215. * @private
  1216. */
  1217. function setInterfaces( _class, _interfaces, _baseInterfaces )
  1218. {
  1219. var interfaces = ( _baseInterfaces &amp;&amp; _baseInterfaces.slice( 0 ) ) || [];
  1220. if ( _interfaces &amp;&amp; _interfaces.length )
  1221. {
  1222. // Validate the interfaces specified are indeed interfaces.
  1223. for ( var idx = 0, len = _interfaces.length; idx &lt; len; ++idx )
  1224. {
  1225. // An interface should derive DIRECTLY from Interface.
  1226. if ( getBase( _interfaces[idx] ) !== Interface )
  1227. throw new Error( "Interface " + idx + " is not a valid interface." );
  1228. if ( interfaces.indexOf( _interfaces[idx] ) &lt; 0 )
  1229. interfaces.push( _interfaces[idx] );
  1230. else if ( !__RELEASE__ &amp;&amp; ( typeof console !== "undefined" ) )
  1231. console.info( describe( _class ) + " declares to implement " + describe( _interfaces[idx] ) + " also implemented by a base class." );
  1232. }
  1233. }
  1234. // Freeze the interfaces array for security
  1235. ObjectPolyfill.defineProperty( _class, "interfaces", { value : ObjectPolyfill.freeze( interfaces ) } );
  1236. }
  1237. /**
  1238. * See if _interface matches _searchInterface. This includes looking at extended interfaces.
  1239. * @param {function} _interface The interface to check
  1240. * @param {function} _searchInterface The interface to look for in _interface's tree.
  1241. * @returns {boolean} True if the interface matches, false otherwise
  1242. */
  1243. function matchInterface( _interface, _searchInterface )
  1244. {
  1245. if ( _interface === _searchInterface )
  1246. return true;
  1247. if ( _interface.interfaces )
  1248. {
  1249. for ( var idx = 0, len = _interface.interfaces.length; idx &lt; len; ++idx )
  1250. {
  1251. if ( matchInterface( _interface.interfaces[idx], _searchInterface ) )
  1252. return true;
  1253. }
  1254. }
  1255. return false;
  1256. }
  1257. /**
  1258. * Wrapper for {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is Object.is} that will "unproxy" values.
  1259. *
  1260. * // This returns false, Object.is sees two different objects:
  1261. * Object.is( instance, instance.as( MyInterface );
  1262. *
  1263. * // This returns true, decl will unproxy proxies.
  1264. * decl.is( instance, instance.as( MyInterface ) );
  1265. *
  1266. * @param _a Any value to check for equality
  1267. * @param _b Any value to check for equality
  1268. * @returns {boolean} True if the values are considered equal, false otherwise.
  1269. * @memberof module:barejs.decl
  1270. */
  1271. function is( _a, _b )
  1272. {
  1273. return Object.is(
  1274. isProxy( _a ) ? _a.as( ObjectKey ) : _a,
  1275. isProxy( _b ) ? _b.as( ObjectKey ) : _b
  1276. );
  1277. }
  1278. /**
  1279. * Check if the _target is an interface.
  1280. * Only works with class constructors, since there should be no instance of an interface.
  1281. * @param {function} _target The class constructor to test if it is an interface.
  1282. * @returns {boolean} True if _target is an interface, false otherwise.
  1283. * @memberof module:barejs.decl
  1284. */
  1285. function isInterface( _target )
  1286. {
  1287. return ( typeof _target === "function" ) &amp;&amp; ( _target.prototype instanceof Interface );
  1288. }
  1289. /**
  1290. * Check if the _target is an enum.
  1291. * Only works with instances, since enums should not expose their class constructor directly.
  1292. * @param {object} _target The instance to test if it is an enum.
  1293. * @returns {boolean} True if _target is an enum, false otherwise.
  1294. * @memberof module:barejs.decl
  1295. */
  1296. function isEnum( _target )
  1297. {
  1298. // Enums should always be an instance
  1299. return _target instanceof Enum;
  1300. }
  1301. /**
  1302. * Check if the target is a proxy object.
  1303. * @param {object} _target The object to check.
  1304. * @returns {boolean} True if _target is a proxy object, false otherwise.
  1305. * @memberof module:barejs.decl
  1306. */
  1307. function isProxy( _target )
  1308. {
  1309. // _target must be an object, instanceof Interface, having an "as" method directly on the object (the "is" method is not tested)
  1310. return ( _target instanceof Interface ) &amp;&amp; _target.hasOwnProperty( "as" );
  1311. }
  1312. /**
  1313. * Checks if _class has _base as a superclass. Also true if _class === _base.
  1314. * Do not use this method to check if an Object is of a specific type, use the instanceof operator
  1315. * for that instead.
  1316. * @param {function} _class The function to check.
  1317. * @param {function} _base The base class to check for.
  1318. * @returns {boolean} True if the class has the base class, false otherwise.
  1319. * @memberof module:barejs.decl
  1320. */
  1321. function hasBase( _class, _base )
  1322. {
  1323. if ( typeof _class !== "function" )
  1324. throw new TypeError( "_class is not a (constructor) function" );
  1325. if ( typeof _base !== "function" )
  1326. throw new TypeError( "_base is not a (constructor) function" );
  1327. return ( _class === _base ) || ( _class.prototype instanceof _base );
  1328. }
  1329. /**
  1330. * Create and return a proxy object for the target
  1331. * @param {object} _target The object to proxy
  1332. * @param {function} _interface The interface defining the members to proxy.
  1333. * @returns {object} The proxy object (instanceof _interface).
  1334. * @memberof module:barejs.decl
  1335. */
  1336. function proxy( _target, _interface )
  1337. {
  1338. if ( !isInterface( _interface ) )
  1339. throw new TypeError( describe( _interface ) + " is not a valid Interface" );
  1340. if ( !_target )
  1341. throw new Error( "Cannot proxy " + describe( _target ) + " as " + describe( _interface ) );
  1342. // Don't proxy a proxy, instead attempt to proxy on the source object
  1343. if ( isProxy( _target ) )
  1344. return proxy( _target.as( ObjectKey ), _interface );
  1345. // Create the proxy object
  1346. var props =
  1347. {
  1348. // Adding the constructor shows proxy objects named as the interface in the debugger
  1349. // (provided the interface function is named)
  1350. constructor: { value : _interface },
  1351. // Add the as and is methods to the proxy, read-only and not enumerated
  1352. as: { value : classProtoExtension.as.value.bind( _target ) },
  1353. is: { value : classProtoExtension.is.value.bind( _target ) }
  1354. };
  1355. for ( var members = InterfaceMetaData.get( _interface ).members, idx = 0, len = members.length, member, prop; idx &lt; len; ++idx )
  1356. {
  1357. switch ( ( member = members[idx] ).type )
  1358. {
  1359. case "function":
  1360. prop = _target[member.name];
  1361. if ( typeof prop !== "function" )
  1362. throw new Error( "as( " + describe( _interface ) + " ) expected " + member + " to be on " + describe( _target.constructor ) + ", but it is missing." );
  1363. // Make the function proxies read-only, enumerable
  1364. props[member.name] = { value : prop.bind( _target ), enumerable : true };
  1365. break;
  1366. case "property":
  1367. if ( !( member.name in _target ) )
  1368. throw new Error( "as( " + describe( _interface ) + " ) expected " + member + " to be on " + describe( _target.constructor ) + ", but it is missing." );
  1369. if ( ObjectPolyfill.propertyGetSetSupport )
  1370. {
  1371. prop = { enumerable: true };
  1372. if ( member.allowGet )
  1373. prop.get = _get.bind( _target, member.name );
  1374. if ( member.allowSet )
  1375. prop.set = _set.bind( _target, member.name );
  1376. // No point in checking for property support: we already checked that in the if above
  1377. props[member.name] = prop;
  1378. }
  1379. else if ( typeof console !== undefined )
  1380. {
  1381. console.warn( "interface proxy skipping " + member + ", since the environment doesn't support getters and setters." );
  1382. }
  1383. break;
  1384. }
  1385. }
  1386. // Attempt to seal the proxy; we don't need to freeze since no properties are configurable
  1387. return ObjectPolyfill.seal( Object.create( _interface.prototype, props ) );
  1388. }
  1389. /**
  1390. * Duck-type check for compliance with an interface
  1391. * @param {object} _target The object to validate
  1392. * @param {function} _interface The interface to check for
  1393. * @returns {boolean} True if the object seems to implement all members
  1394. * @memberof module:barejs.decl~
  1395. * @private
  1396. */
  1397. function duckHasInterface( _target, _interface )
  1398. {
  1399. var valid = !!_target;
  1400. if ( valid )
  1401. {
  1402. // Ensure object
  1403. _target = Object( _target );
  1404. for ( var m = InterfaceMetaData.get( _interface ).members, i = m.length - 1; valid &amp;&amp; i >= 0; --i )
  1405. {
  1406. switch( m[i].type )
  1407. {
  1408. case "function":
  1409. valid = typeof _target[m[i].name] === "function";
  1410. break;
  1411. case "property":
  1412. valid = m[i].name in _target;
  1413. break;
  1414. }
  1415. }
  1416. }
  1417. return valid;
  1418. }
  1419. /**
  1420. * Checks if _target (or a base class) implements the specified Interface.
  1421. * Works on both instances and the class constructor.
  1422. * @param {object|Function} _target The object or class constructor to check.
  1423. * @param {function} _interface The interface to check for. If interface is not a valid interface,
  1424. * this method always returns false.
  1425. * @param {boolean} [_strict=false] Optional: set to true to avoid a duck-type check, and only check decl
  1426. * metadata for interfaces implemented.
  1427. * @returns {boolean} True if _target (or a base class) implements the interface, false otherwise.
  1428. * @memberof module:barejs.decl
  1429. */
  1430. function hasInterface( _target, _interface, _strict )
  1431. {
  1432. if ( !isInterface( _interface ) )
  1433. throw new TypeError( "hasInterface: _interface must be an interface defined with decl.declareInterface, but is " + describe( _interface ) );
  1434. if ( !_target )
  1435. return false;
  1436. // Detect proxies
  1437. if ( isProxy( _target ) )
  1438. return hasInterface( _target.as( ObjectKey ), _interface, _strict );
  1439. var isFn = typeof _target === "function";
  1440. // Walk up the inheritance tree
  1441. for ( var base = isFn ? _target : _target.constructor; base &amp;&amp; ( base !== Object ) &amp;&amp; ( base !== Interface ) &amp;&amp; ( base !== Enum ); base = getBase( base ) )
  1442. {
  1443. if ( base.interfaces &amp;&amp; matchInterface( base, _interface ) )
  1444. return true;
  1445. }
  1446. // Resort to duck-type check
  1447. return ( _strict !== true ) &amp;&amp; duckHasInterface( isFn ? _target.prototype : _target, _interface );
  1448. }
  1449. //
  1450. // Start of Declare methods
  1451. //
  1452. /**
  1453. * Helper method that unifies errors returned by declare* methods.
  1454. * @param {string} _name The name of the function from which the error is triggered
  1455. * @param {Array} _arguments or (arguments object) of arguments
  1456. * @param {string} _message The custom part of the message
  1457. * @returns {string} The error message
  1458. * @memberof module:barejs.decl~
  1459. * @private
  1460. */
  1461. function formatErrorMessage( _name, _arguments, _message )
  1462. {
  1463. return _name + "( " + Array.prototype.map.call( _arguments, describe ).join( ", " ) + " ): " + _message;
  1464. }
  1465. /**
  1466. * Helper method that will validate the _class argument passed to declare(Class|Interface|Enum).
  1467. * Ensures consistent validation across methods.
  1468. * @param {function} _class The _class argument as received by the declare method.
  1469. * @param {string} _method The name of the method requesting the validation.
  1470. * @param {Array} _arguments The arguments passed to the requesting method.
  1471. * @memberof module:barejs.decl~
  1472. * @private
  1473. */
  1474. function validateClassArg( _class, _method, _arguments )
  1475. {
  1476. // Validation
  1477. if ( !_class || ( typeof _class !== "function" ) )
  1478. throw new TypeError( formatErrorMessage( _method, _arguments, "_class is not a function." ) );
  1479. // Some special methods don't have a prototype, warn when these are passed to declareClass
  1480. if ( !( "prototype" in _class ) )
  1481. throw new Error( formatErrorMessage( _method, _arguments, "_class doesn't have a prototype property; it is probably a built-in function." ) );
  1482. if ( ( "superclass" in _class ) || ( "interfaces" in _class ) )
  1483. throw new Error( formatErrorMessage( _method, _arguments, "The " + describe( _class ) + " is already declared, cannot perform " + _method + "." ) );
  1484. if ( native_ctors.indexOf( _class ) >= 0 )
  1485. throw new Error( formatErrorMessage( _method, _arguments, "Attempt to call " + _method + " on built-in type" + ( _class.name ? " " + _class.name + "." : "" ) ) );
  1486. // Note: this check can fail if the base prototype can not be detected. We still perform it as
  1487. // a convenience for detecting errors, and hope the check for existing properties on the
  1488. // prototype will catch the other cases.
  1489. var base = getBase( _class );
  1490. if ( base &amp;&amp; ( base !== Object ) )
  1491. throw new Error( formatErrorMessage( _method, _arguments, "_class already has a base " + describe( base ) + "." ) );
  1492. // Throw an error if anything has been defined on the original prototype
  1493. checkCleanPrototype( _class, _method );
  1494. }
  1495. /**
  1496. * Helper method that will pop the last value of an array if it's not a function.
  1497. * If the last argument is a function, or the array is empty, the _array is not modified and null is returned.
  1498. * @param {Array} _array The array to pop the non-function from.
  1499. * @memberof module:barejs.decl~
  1500. * @private
  1501. */
  1502. function popNonFunction( _array )
  1503. {
  1504. var len = _array.length;
  1505. return ( len > 0 ) &amp;&amp; ( typeof _array[len - 1] !== "function" ) ? Object( _array.pop() ) : null;
  1506. }
  1507. /**
  1508. * Helper method that perform the actual class definition for both abstractClass and declareClass.
  1509. * @param {String} _definingName The function that is requesting a class be made, e.g. abstractClass or declareClass.
  1510. * @param {arguments} _args The arguments passed along to the declare function.
  1511. * @param {Boolean} [_validate=false] Optional: whether interfaces should be validated for implementation.
  1512. * @memberof module:barejs.decl~
  1513. * @private
  1514. */
  1515. function makeClass( _definingName, _args, _validate )
  1516. {
  1517. // normalize optional interfaces and/or prototype properties
  1518. var cls = _args[0],
  1519. base = null,
  1520. interfaces = null,
  1521. stat = null,
  1522. proto = null;
  1523. /*istanbul ignore else: We always test in DEBUG*/
  1524. if ( !__RELEASE__ )
  1525. validateClassArg( cls, _definingName, _args );
  1526. if ( _args.length > 1 )
  1527. {
  1528. interfaces = slice.call( _args, 1, _args.length );
  1529. // If the first additional argument is null or a constructor function (not an interface), it is a base class.
  1530. if ( ( interfaces[0] === null ) || ( ( typeof interfaces[0] === "function" ) &amp;&amp; ( !isInterface( interfaces[0] ) ) ) )
  1531. base = interfaces.shift();
  1532. // If the last argument is not a function, assume it's a prototype definition.
  1533. proto = popNonFunction( interfaces );
  1534. // If the second to last argument is not a function, assume it's a static definition.
  1535. stat = popNonFunction( interfaces );
  1536. }
  1537. /*istanbul ignore else: We always test in DEBUG*/
  1538. if ( !__RELEASE__ )
  1539. {
  1540. // If a base class is passed, validate it too
  1541. if ( base )
  1542. {
  1543. if ( typeof base !== "function" )
  1544. {
  1545. throw new TypeError( formatErrorMessage( _definingName, _args, "_base is not a function.\r\n" +
  1546. "If you are passing a prototype definition, specify null as second argument." ) );
  1547. }
  1548. if ( cls === base )
  1549. throw new Error( formatErrorMessage( _definingName, _args, "A class cannot extend itself." ) );
  1550. if ( !( "prototype" in base ) )
  1551. throw new Error( formatErrorMessage( _definingName, _args, "base doesn't have a prototype property; it is probably a built-in function." ) );
  1552. if ( hasBase( base, Enum ) )
  1553. throw new Error( formatErrorMessage( _definingName, _args, "Cannot extend an enum. To create an enum, use declareEnum." ) );
  1554. if ( hasBase( base, Interface ) )
  1555. {
  1556. throw new Error( formatErrorMessage( _definingName, _args,
  1557. "Cannot extend an interface.\r\n" +
  1558. "To declare implementing interfaces, add them as arguments after _base; passing null as base class.\r\n" +
  1559. "To create an interface extending another interface, use declareInterface instead."
  1560. ) );
  1561. }
  1562. }
  1563. }
  1564. if ( base === null )
  1565. base = Object;
  1566. derive( cls, base, expandDefineProperties( proto, base &amp;&amp; base.prototype, toDefineProperty, ( cls.name || "(Class)" ) + ".prototype" ) );
  1567. setInterfaces( cls, interfaces, base.interfaces );
  1568. // Apply static definition
  1569. if ( base !== Object || stat )
  1570. applyStatic( cls, base, stat );
  1571. // If not inherited from a base class, add certain utility methods like "is" and "as" to the class.
  1572. if ( !( "as" in cls.prototype ) )
  1573. ObjectPolyfill.defineProperties( cls.prototype, classProtoExtension );
  1574. // If we or our base have any interfaces, and if we're validating
  1575. /*istanbul ignore else: We always test in DEBUG*/
  1576. if ( !__RELEASE__ &amp;&amp; _validate &amp;&amp; ( cls.interfaces.length > 0 ) )
  1577. {
  1578. // If we have a prototype definition ...
  1579. if ( proto )
  1580. {
  1581. // validate immediately.
  1582. validateInterfacesImplemented( cls );
  1583. }
  1584. else
  1585. {
  1586. // We can't validate immediately (since methods are expected to be added via
  1587. // MyClass.prototype.method = function() {}; calls), so we queue validation.
  1588. _validate.push( cls );
  1589. if ( !( "timeout" in _validate ) )
  1590. _validate.timeout = setTimeout( handleValidateQueue, 1 );
  1591. }
  1592. }
  1593. return cls;
  1594. }
  1595. /**
  1596. * Declare a constructor function to be an abstract class. Allows specifying an optional base class and interfaces implemented.
  1597. * When reading or writing decl.abstractClass statements, it might help to know it was designed to mimic popular languages
  1598. * in its format. Here's an example:
  1599. *
  1600. * // With common languages
  1601. * abstract class ClassName : BaseClass, Interface1, Interface2 // C#
  1602. * abstract class ClassName extends BaseClass implements Interface1, Interface2 // Java
  1603. * {
  1604. * private String _name;
  1605. *
  1606. * public ClassName( _name )
  1607. * {
  1608. * super( 42 );
  1609. * this._name = _name;
  1610. * }
  1611. *
  1612. * public String toString()
  1613. * {
  1614. * return this._name;
  1615. * }
  1616. * }
  1617. *
  1618. * // With barejs.decl:
  1619. *
  1620. * function ClassName( _name )
  1621. * {
  1622. * BaseClass.call( this, 42 );
  1623. * this._name = _name;
  1624. * }
  1625. *
  1626. * decl.abstractClass( ClassName, BaseClass, Interface1, Interface2,
  1627. * {
  1628. * // This puts the _name property as null on the prototype,
  1629. * // which is purely for clarity (e.g. stating it exists).
  1630. * _name: null,
  1631. *
  1632. * toString: function()
  1633. * {
  1634. * return this._name;
  1635. * }
  1636. * }
  1637. *
  1638. * - If a base class is provided, decl will ensure _class.prototype is instanceof _base.prototype.
  1639. * - For abstract classes, decl will not validate if interface methods are implemented.
  1640. * - If a _static argument is specified, it will be applied to the constructor function using {@link module:barejs.decl.defineObject defineObject}.
  1641. * - If a _prototype argument is specified, it will be applied to the prototype using {@link module:barejs.decl.defineObject defineObject}.
  1642. * - If only 1 object is supplied, it is always interpreted as prototype definition, never as static.
  1643. * You must specify null for the prototype object if you only want to specify static members.
  1644. *
  1645. * By default, any static members (except `name`, `prototype`, `constructor`, `superclass` and other metadata) will inherit.
  1646. * A class can make static functions "private" by defining a static `$private` function, accepting a String/Symbol as key.
  1647. * This function should return `true` for any keys that should *not* inherit. The `$private` function itself will never inherit.
  1648. *
  1649. * @param {function} _class The constructor function of the class
  1650. * @param {function} [_base] Optional: Extended base class
  1651. * @param {...function} [_interface] Optional: Any number of implemented interfaces
  1652. * @param {object} [_static] Optional: static definition; properties that will be added to the constructor function.
  1653. * @param {object} [_prototype] Optional: prototype definition: properties that will be added to the prototype
  1654. * @returns {function} The constructor function (**_class**), so it can immediately be returned
  1655. * @memberof module:barejs.decl
  1656. */
  1657. function abstractClass( _class/*[, _base] [, _interface...] [, _static] [, _prototype] */ )
  1658. {
  1659. return makeClass( "abstractClass", arguments );
  1660. }
  1661. /**
  1662. * Declare a constructor function to be a class. Allows specifying an optional base class and interfaces implemented.
  1663. * When reading or writing decl.declareClass statements, it might help to know it was designed to mimic popular languages
  1664. * in its format. Here's an example:
  1665. *
  1666. * // With common languages
  1667. * class ClassName : BaseClass, Interface1, Interface2 // C#
  1668. * class ClassName extends BaseClass implements Interface1, Interface2 // Java
  1669. * {
  1670. * private String _name;
  1671. *
  1672. * public ClassName( _name )
  1673. * {
  1674. * super( 42 );
  1675. * this._name = _name;
  1676. * }
  1677. *
  1678. * public String toString()
  1679. * {
  1680. * return this._name;
  1681. * }
  1682. * }
  1683. *
  1684. * // With barejs.decl:
  1685. *
  1686. * function ClassName( _name )
  1687. * {
  1688. * BaseClass.call( this, 42 );
  1689. * this._name = _name;
  1690. * }
  1691. *
  1692. * decl.declareClass( ClassName, BaseClass, Interface1, Interface2,
  1693. * {
  1694. * // This puts the _name property as null on the prototype,
  1695. * // which is purely for clarity (e.g. stating it exists).
  1696. * _name: null,
  1697. *
  1698. * toString: function()
  1699. * {
  1700. * return this._name;
  1701. * }
  1702. * }
  1703. *
  1704. * - If a base class is provided, decl will ensure _class.prototype is instanceof _base.prototype.
  1705. * - If interfaces are declared, decl will validate methods are implemented.
  1706. * - If a _static argument is specified, it will be applied to the constructor function using {@link module:barejs.decl.defineObject defineObject}.
  1707. * - If a _prototype argument is specified, it will be applied to the prototype using {@link module:barejs.decl.defineObject defineObject}.
  1708. * - If only 1 object is supplied, it is always interpreted as prototype definition, never as static.
  1709. * You must specify null for the prototype object if you only want to specify static members.
  1710. *
  1711. * By default, any static members (except `name`, `prototype`, `constructor`, `superclass` and other metadata) will inherit.
  1712. * A class can make static functions "private" by defining a static `$private` function, accepting a String/Symbol as key.
  1713. * This function should return `true` for any keys that should *not* inherit. The `$private` function itself will never inherit.
  1714. *
  1715. * @param {function} _class The constructor function of the class
  1716. * @param {function} [_base] Optional: Extended base class
  1717. * @param {...function} [_interface] Optional: Any number of implemented interfaces
  1718. * @param {object} [_static] Optional: static definition; properties that will be added to the constructor function.
  1719. * @param {object} [_prototype] Optional: prototype definition: properties that will be added to the prototype
  1720. * @returns {function} The constructor function (**_class**), so it can immediately be returned
  1721. * @memberof module:barejs.decl
  1722. */
  1723. function declareClass( _class/*[, _base] [, _interface...] [, _static] [, _prototype] */ )
  1724. {
  1725. return makeClass( "declareClass", arguments, validateQueue );
  1726. }
  1727. /**
  1728. * Declare an interface. An interface can extend multiple interfaces by passing them as additional parameters.
  1729. * The constructor function _class will be made to derive from the special internal {@link module:barejs.decl~Interface Interface} type.
  1730. * It is not meant to be instantiated, and decl will never call the interface's constructor.
  1731. * @param {function} _class The "constructor" function to declare as an interface.
  1732. * @param {...function} [_interface] Any number of interfaces to extend.
  1733. * @param {object} [_static] Optional: static definition; properties that will be added to the constructor function.
  1734. * @param {object} [_prototype] Optional: prototype to apply
  1735. * @returns {function} The interface definition (_class).
  1736. * @memberof module:barejs.decl
  1737. */
  1738. function declareInterface( _class /*[, _interface...] [, _static] [, _prototype] */ )
  1739. {
  1740. var interfaces = null,
  1741. stat = null,
  1742. proto = null;
  1743. // Development time validation
  1744. /*istanbul ignore else: We always test in DEBUG*/
  1745. if ( !__RELEASE__ )
  1746. validateClassArg( _class, "declareInterface", arguments );
  1747. // If the interface extends other interfaces, set up the inheritance.
  1748. if ( arguments.length > 1 )
  1749. {
  1750. interfaces = slice.call( arguments, 1, arguments.length );
  1751. // If the last argument is not a function, assume it's a prototype definition.
  1752. proto = popNonFunction( interfaces );
  1753. // If the second to last argument is not a function, assume it's a static definition.
  1754. stat = popNonFunction( interfaces );
  1755. }
  1756. // Each interface derives directly from Interface so it is easy to detect interfaces.
  1757. derive( _class, Interface, expandDefineProperties( proto, Interface.prototype, toDefinePropertyInterface, _class.name || "(Interface)" ) );
  1758. setInterfaces( _class, interfaces, null );
  1759. if ( stat )
  1760. applyStatic( _class, null, stat );
  1761. // Development time validation
  1762. /*istanbul ignore else: We always test in DEBUG*/
  1763. if ( !__RELEASE__ &amp;&amp; validateQueue )
  1764. {
  1765. // If we don't have a prototype definition ...
  1766. if ( proto === null )
  1767. {
  1768. // We can't validate immediately (since methods are expected to be added via
  1769. // MyInterface.prototype.method = function() {}; calls), so we queue validation.
  1770. // Prepend interfaces so they get validated first
  1771. validateQueue.unshift( _class );
  1772. if ( !( "timeout" in validateQueue ) )
  1773. validateQueue.timeout = setTimeout( handleValidateQueue, 1 );
  1774. }
  1775. else
  1776. {
  1777. // Otherwise just validate immediately.
  1778. // Creating the interface metadata will perform validation of the interface.
  1779. InterfaceMetaData.get( _class );
  1780. }
  1781. }
  1782. return _class;
  1783. }
  1784. /**
  1785. * Declare an enum. decl will make _class derive from the special internal {@link module:barejs.decl~Enum Enum} type,
  1786. * and return a new instance of it. Enum values should be set on 'this' in the constructor, utility methods should be
  1787. * added to the prototype.
  1788. *
  1789. * var SampleEnum = decl.declareEnum( function SampleEnum()
  1790. * {
  1791. * this.Bit1 = 1;
  1792. * this.Bit2 = 2;
  1793. * this.Bit3 = 4;
  1794. * this.Bit4 = 8;
  1795. * },
  1796. * // End of constructor, what follows is the prototype definition:
  1797. * {
  1798. * hasBit: function( _bit, _value )
  1799. * {
  1800. * // hasValue is provided by the Enum base class
  1801. * if ( !this.hasValue( _bit ) )
  1802. * throw new TypeError( "Unknown SampleEnum value: " + _bit );
  1803. * return _value &amp; _bit === _bit
  1804. * }
  1805. * } );
  1806. *
  1807. * // SampleEnum is now instanceof the SampleEnum function passed to decl.
  1808. * SampleEnum.hasBit( SampleEnum.Bit2, 3 ); // true
  1809. * // And it inherited decl's Enum type members
  1810. * SampleEnum.names(); // ["Bit1", "Bit2", "Bit3", "Bit4"]
  1811. * SampleEnum instanceof Object; // false
  1812. *
  1813. * Note that the prototype property, if specified, is applied using {@link module:barejs.decl.defineObject defineObject}.
  1814. * @param {function} _class The "constructor" function to declare as an Enum.
  1815. * @param {object} [_prototype] Optional: things to add to the enum prototype.
  1816. * @returns {object} The enum instance (instanceof _class).
  1817. * @memberof module:barejs.decl
  1818. */
  1819. function declareEnum( _class/*[, _prototype]*/ )
  1820. {
  1821. /*istanbul ignore else: We always test in DEBUG*/
  1822. if ( !__RELEASE__ )
  1823. validateClassArg( _class, "declareEnum", arguments );
  1824. // An enum inherits directly from Enum, so they can be easily detected (and receive some helper methods).
  1825. derive( _class, Enum, expandDefineProperties( arguments[1], Enum.prototype, toDefineProperty, ( _class.name || "(Enum)" ) ) );
  1826. // jshint -W055
  1827. // Return an instance for an enum
  1828. return ObjectPolyfill.freeze( new _class() );
  1829. // jshint +W055
  1830. }
  1831. /**
  1832. * defineObject is similar to {@link module:barejs.decl.defineProperties decl.defineProperties}, but it expands the _definition's properties if needed.
  1833. * It will update values of properties that are not property assigment definitions to be proper property definitions, defaulting to:
  1834. *
  1835. * { configurable: false, writable: true, enumerable: true, value: &amp;lt;value&amp;gt; }
  1836. *
  1837. * (Note: enumerable will be `false` if the name starts with _ or is a Symbol).
  1838. * defineObject will iterate the _definition object and expand properties on it. Please be aware of the following:
  1839. * 1. **_destination** will be modified. If that's a problem, use {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign Object.assign} to create a copy first.
  1840. * 2. Existing properties are scanned for presence of a **value**, **get** or **set** property. If these are present on a value, you **must** use the full property syntax:
  1841. *
  1842. * decl.defineObject( MyClass,
  1843. * {
  1844. * // Since the object we want to assign to the MyClass constructor function has properties that make it look like
  1845. * // a property definition, we have to wrap it in a property definition as required by Object.defineProperties.
  1846. * staticFlags: { enumerable: true, value: { get: true, set: false } }
  1847. * } );
  1848. *
  1849. * 3. defineObject will silently ignore getter/setter properties in environments that don't support them, unlike {@link module:barejs.decl.defineProperties}.
  1850. * 4. You can reference a getter or setter by name, provided it is also on the definition object:
  1851. *
  1852. * decl.defineObject( {},
  1853. * {
  1854. * // underlying data Array, not enumerable, configurable or writable
  1855. * _values: { value: [] },
  1856. * // getSize function that returns the length of the _values
  1857. * getSize: function() { return this._values.length; },
  1858. * // Size property. Refers to the getSize function for the getter, instead of using an inline function.
  1859. * size: { enumerable: true, get: "getSize" }
  1860. * } );
  1861. *
  1862. * @param {object} _target The target object
  1863. * @param {object} _definition The definitions to assign to _target. Note that _definition will be modified to contain property definitions(!).
  1864. * @param {string} [_objectName] Optional: the name of the object. If passed, decl will generate displayName properties on methods for an enhanced debugging experience.
  1865. * For example: if "decl" is passed as name, and there's an "is" function on _definition, the displayName of the "is" function will be set to "decl.is").
  1866. * @returns _target The target object, expanded
  1867. * @memberof module:barejs.decl
  1868. */
  1869. function defineObject( _target, _definition/*, _objectName*/ )
  1870. {
  1871. if ( !_definition )
  1872. throw new Error( "Missing definition" );
  1873. return ObjectPolyfill.defineProperties( _target, expandDefineProperties( _definition, _target, toDefineProperty, arguments.length > 2 ? String( arguments[2] ) : _target.name ) );
  1874. }
  1875. /**
  1876. * Interpret `_target` as implementing a Java Functional Interface. A functional interface is an Interface with exactly
  1877. * 1 function defined. Java allows lambda expressions to be generated for arguments of this type of interface.
  1878. * This method allows normalizing a function or object to a "Functional Interface Object", so Java behavior can be
  1879. * emulated.
  1880. * This function will accept:
  1881. * - `null`/`undefined` (then `null` is returned)
  1882. * - A function (an 'instance' of _functionalInterface is returned, with this function in place).
  1883. * - An object complying with _functionalInterface
  1884. *
  1885. * Any other argument will throw a {@link TypeError}.
  1886. * @param {object|function} _target The target object or function.
  1887. * @param {function} _functionalInterface The interface to use as functional interface. May only have a single method defined.
  1888. * @param {boolean} [_strict=false] Optional: set to true to avoid a duck-type check, and only check `decl`
  1889. * metadata for interfaces implemented.
  1890. * @returns Either an object compliant with `_functionalInterface`, or `null`.
  1891. * @throws {TypeError} A TypeError may occur if `_functionalInterface` is not a valid functional interface,
  1892. * or if _target does not comply with the interface.
  1893. * @memberof module:barejs.decl
  1894. */
  1895. function asFunctional( _target, _functionalInterface, _strict )
  1896. {
  1897. if ( !isInterface( _functionalInterface ) )
  1898. throw new TypeError( _functionalInterface + " is not an interface" );
  1899. var meta = InterfaceMetaData.get( _functionalInterface );
  1900. var fn = meta.members[ 0 ];
  1901. if ( meta.members.length !== 1 || fn.type !== "function" )
  1902. throw new TypeError( _functionalInterface.prototype + " is not a functional interface, functional interfaces have a single method" );
  1903. if ( _target === null || _target === undefined )
  1904. return null;
  1905. // If this is a function, return an object that can be used instead.
  1906. if ( typeof _target === "function" )
  1907. {
  1908. var def = {};
  1909. def[fn.name] = { enumerable: true, value: _target };
  1910. return Object.create( _functionalInterface.prototype, def );
  1911. }
  1912. if ( hasInterface( _target, _functionalInterface, _strict ) )
  1913. return _target;
  1914. throw new TypeError( _target + " does not implement " + _functionalInterface.prototype );
  1915. }
  1916. defineObject( exports,
  1917. {
  1918. // Exports
  1919. isInterface: isInterface,
  1920. isEnum: isEnum,
  1921. isProxy: isProxy,
  1922. is: is,
  1923. hasBase: hasBase,
  1924. hasInterface: hasInterface,
  1925. proxy: proxy,
  1926. abstractClass: abstractClass,
  1927. declareClass: declareClass,
  1928. declareInterface: declareInterface,
  1929. declareEnum: declareEnum,
  1930. defineObject: defineObject,
  1931. asFunctional: asFunctional,
  1932. // Convenience properties to define interface properties
  1933. readOnlyProperty: readOnlyProperty,
  1934. readWriteProperty: readWriteProperty,
  1935. // Allows certain low level classes to disallow casting to them
  1936. preventCast: preventCast
  1937. }, "decl" );
  1938. // We do NOT want to add a displayName to methods from other modules, so use a separate defineObject
  1939. defineObject( exports,
  1940. /** @lends module:barejs.decl */
  1941. {
  1942. // Helper methods/flags for property definitions
  1943. /**
  1944. * This convenience property is true if the environment supports property getters and setters.
  1945. * @member {boolean}
  1946. */
  1947. hasPropertySupport: ObjectPolyfill.propertyGetSetSupport,
  1948. /**
  1949. * decl re-exports {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty Object.defineProperty}.
  1950. *
  1951. * If the native version is not available, a fall-back is used. The fallback supports the same syntax as the original, but falls back to simple assignment
  1952. * or deprecated constructs like {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/__defineGetter__ __defineGetter__}.
  1953. * Be aware that property getters or setters may not be supported in some environments, which is indicated by {@link module:barejs.decl.hasPropertySupport hasPropertySupport} being false.
  1954. * @function
  1955. * @param {object} _target The object to define the property on
  1956. * @param {string|Symbol} _key The name or {@link module:barejs.Symbol Symbol} to set
  1957. * @param {object} _definition Object containing either a **value** property, or a **get** and/or **set** property (function).
  1958. * @param {function} [_definition.get] A getter function, taking no arguments and returning the property value.
  1959. * @param {function} [_definition.set] A setter function, taking a value as argument.
  1960. * @param {*} [_definition.value] The value to assign to the property.
  1961. * @param {boolean} [_definition.writable=false] (Only in combination with value) Whether assigning to the property is allowed.
  1962. * For properties with get/set, the writable is implicit (by absence or presence of a setter function).
  1963. * @param {boolean} [_definition.configurable=false] Whether the property may be altered via delete or a next defineProperty call.
  1964. * @param {boolean} [_definition.enumerable=false] Whether the property shows up in object property enumerations.
  1965. * @returns {object} The object the properties where defined on (_target).
  1966. */
  1967. defineProperty: ObjectPolyfill.defineProperty,
  1968. /**
  1969. * decl re-exports {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties Object.defineProperties}.
  1970. *
  1971. * If the native version is not available, a fall-back is used. The fallback supports the same syntax as the original, and uses the {@link module:barejs.decl.defineProperty defineProperty} fallback.
  1972. * @function
  1973. * @param {object} _target The object to define the property on
  1974. * @param {object} _definitions Object containing properties, each of which have a value that is a definition as passed to defineProperty.
  1975. * @returns {object} The object the properties where defined on (_target).
  1976. */
  1977. defineProperties: ObjectPolyfill.defineProperties,
  1978. // Helper methods for object strictness (seal/freeze)
  1979. /**
  1980. * decl re-exports {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal Object.seal}.
  1981. *
  1982. * If the native version is not available, a **no-operation** function is used. The object is not altered in any way and simply returned.
  1983. * @function
  1984. * @param {object} _target The object to seal.
  1985. * @returns {object} The object that was passed (_target).
  1986. */
  1987. seal: ObjectPolyfill.seal,
  1988. /**
  1989. * decl re-exports {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed Object.isSealed}.
  1990. * A sealed object may not be altered by adding or removing properties. Existing properties may be altered (provided they are writable).
  1991. *
  1992. * If the native version is not available, a **no-operation** function is used. The object is not altered by the fallback, and it always returns false (since sealing objects is not supported).
  1993. *
  1994. * @function
  1995. * @param {object} _target The object to evaluate.
  1996. * @returns {boolean} True if the object (_target) is sealed, false otherwise. The fallback always returns false.
  1997. */
  1998. isSealed: ObjectPolyfill.isSealed,
  1999. /**
  2000. * decl re-exports {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze Object.freeze}.
  2001. * A frozen object may not be altered in any way. No properties may be added or removed (like seal), and all values are made read-only.
  2002. *
  2003. * If the native version is not available, a **no-operation** function is used. The object is not altered in any way and simply returned.
  2004. *
  2005. * @function
  2006. * @param {object} _target The object to freeze.
  2007. * @returns {object} The object that was passed (_target).
  2008. */
  2009. freeze: ObjectPolyfill.freeze,
  2010. /**
  2011. * decl re-exports {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen Object.isFrozen}.
  2012. *
  2013. * If the native version is not available, a **no-operation** function is used. The object is not altered by the fallback, and it always returns false (since freezing objects is not supported).
  2014. * @function
  2015. * @param {object} _target The object to evaluate.
  2016. * @returns {boolean} True if the object (_target) is frozen, false otherwise. The fallback always returns false.
  2017. */
  2018. isFrozen: ObjectPolyfill.isFrozen
  2019. } /*DO NOT ADD NAME*/ );
  2020. exports.freeze( exports );
  2021. // End of define
  2022. }(
  2023. Object,
  2024. Array,
  2025. String,
  2026. Error,
  2027. TypeError,
  2028. require( "./polyfill/Object" ),
  2029. require( "./NMap" ),
  2030. require( "./NSet" ),
  2031. require( "./Symbol" ),
  2032. require( "./WeakMap" ),
  2033. // Unreferenced: decl depends on the Array polyfills too.
  2034. require( "./polyfill/Array" )
  2035. ) );
  2036. </code></pre>
  2037. </article>
  2038. </section>
  2039. </div>
  2040. <nav>
  2041. <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-barejs.html">barejs</a></li><li><a href="module-barejs_polyfill.html">barejs/polyfill</a></li><li><a href="module-barejs_polyfill_Intl.html">barejs/polyfill/Intl</a></li></ul><h3>Classes</h3><ul><li><a href="module-barejs.decl.html">decl</a></li><li><a href="module-barejs.decl-Enum.html">Enum</a></li><li><a href="module-barejs.decl-Interface.html">Interface</a></li><li><a href="module-barejs.decl-SpecialType.html">SpecialType</a></li><li><a href="module-barejs.Destroyable.html">Destroyable</a></li><li><a href="module-barejs.EventArgs.html">EventArgs</a></li><li><a href="module-barejs.Evented.html">Evented</a></li><li><a href="module-barejs.Evented-EventedHandle.html">EventedHandle</a></li><li><a href="module-barejs.Exception.html">Exception</a></li><li><a href="module-barejs_polyfill.Array.html">Array</a></li><li><a href="module-barejs_polyfill.Date.html">Date</a></li><li><a href="module-barejs_polyfill.EntryStore.html">EntryStore</a></li><li><a href="module-barejs_polyfill.EntryStore.Iterator.html">Iterator</a></li><li><a href="module-barejs_polyfill.Function.html">Function</a></li><li><a href="module-barejs_polyfill.Map.html">Map</a></li><li><a href="module-barejs_polyfill.Map-MapIterator.html">MapIterator</a></li><li><a href="module-barejs_polyfill.Math.html">Math</a></li><li><a href="module-barejs_polyfill.Number.html">Number</a></li><li><a href="module-barejs_polyfill.Object.html">Object</a></li><li><a href="module-barejs_polyfill.Promise.html">Promise</a></li><li><a href="module-barejs_polyfill.Set.html">Set</a></li><li><a href="module-barejs_polyfill.Set-SetIterator.html">SetIterator</a></li><li><a href="module-barejs_polyfill.String.html">String</a></li><li><a href="module-barejs_polyfill.Symbol.html">Symbol</a></li><li><a href="module-barejs_polyfill.WeakMap.html">WeakMap</a></li><li><a href="module-barejs_polyfill.WeakSet.html">WeakSet</a></li><li><a href="module-barejs_polyfill_Intl.DateTimeFormat.html">DateTimeFormat</a></li><li><a href="module-barejs_polyfill_Intl.DateTimeFormat-DateTimeFormatOptions.html">DateTimeFormatOptions</a></li><li><a href="module-barejs_polyfill_Intl.NumberFormat.html">NumberFormat</a></li><li><a href="module-barejs_polyfill_Intl.NumberFormat-NumberFormatOptions.html">NumberFormatOptions</a></li><li><a href="module-barejs_polyfill_Intl-Format.html">Format</a></li></ul>
  2042. </nav>
  2043. <br class="clear">
  2044. <footer>
  2045. Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Wed Oct 03 2018 15:59:33 GMT+0200 (W. Europe Daylight Time)
  2046. </footer>
  2047. <script> prettyPrint(); </script>
  2048. <script src="scripts/linenumber.js"> </script>
  2049. </body>
  2050. </html>