New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

cyclonejs

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cyclonejs - npm Package Compare versions

Comparing version 0.0.2 to 1.0.0

component.json

285

cyclone.js

@@ -1,9 +0,11 @@

// Cyclone.js: An Adaptation of the HTML5 structured cloning alogrithm.
//
// Can recursively clone objects, including those containing number, boolean,
// string, date, and regex objects. It can also clone objects which included
// cyclic references to itself, including nested cyclic references.
//
// Works in ES5-compatible environments.
/**
* Cyclone.js: An Adaptation of the HTML5 structured cloning alogrithm.
* @author Travis Kaufman <travis.kaufman@gmail.com>
* @license MIT.
*/
// This module can recursively clone objects, including those containing
// number, boolean, string, date, and regex objects. It can also clone objects
// which include cyclic references to itself, including nested cyclic
// references. It is tested in all ES5-compatible environments.
(function(root) {

@@ -13,61 +15,95 @@ 'use strict';

var __call__ = Function.prototype.call;
// Many environments seem to not support nativeBind as of now so because of
// this we'll use our own implementation.
var _bind = function(fn, ctx) {
var slice = [].slice;
var _hasOwn = _bind(__call__, {}.hasOwnProperty);
var _toString = _bind(__call__, {}.toString);
var _slice = _bind(__call__, [].slice);
// Many environments seem to not support ES5's native bind as of now.
// Because of this, we'll use our own implementation.
function _bind(fn, ctx) {
// Get a locally-scoped version of _slice here.
var _slice = [].slice;
// Like native bind, an arbitrary amount of arguments can be passed into
// this function which will automatically be bound to it whenever it's
// called.
var boundArgs = slice.call(arguments, 2);
var boundArgs = _slice.call(arguments, 2);
return function() {
return fn.apply(ctx, boundArgs.concat(slice.call(arguments)));
return fn.apply(ctx, boundArgs.concat(_slice.call(arguments)));
};
};
var _hasProp = _bind(__call__, {}.hasOwnProperty);
var _toString = _bind(__call__, {}.toString);
}
// Utilities for working with transfer maps. A transfer map is defined as an
// object that has two properties, `inputs` and `outputs`, each of which
// are arrays where for any object at inputs[i], the output value that should
// be mapped to the cloned object for that object resides at outputs[i]. See
// the W3C spec for more details. This was the closest I could get without
// having to set custom properties on objects, which wouldn't work for
// immutable objects anyway.
function TransferMap() {
this.inputs = [];
this.outputs = [];
function _isFunc(obj) {
return (typeof obj === 'function');
}
// Map a given `input` object to a given `output` object. Relatively
// straightforward.
TransferMap.prototype.set = function(input, output) {
// We only want to set a reference if the reference already doesn't exist.
// This is included for defensive reasons.
if (this.inputs.indexOf(input) === -1) {
this.inputs.push(input);
this.outputs.push(output);
}
// Quick and dirty shallow-copy functionality for options hash
function _mergeParams(src/*, target1, ..., targetN*/) {
return _slice(arguments, 1).reduce(function(target, mixin) {
for (var key in mixin) {
if (_hasOwn(mixin, key) && !_hasOwn(target, key)) {
target[key] = mixin[key];
}
}
return target;
}, src);
}
// We shim ES6's Map here if it's not in the environment already. Although
// it would be better to use WeakMaps here, this is impossible to do with ES5
// since references to objects won't be garbage collected if they're still
// in the map, so it's better to keep the implementation consistent.
var Map = _isFunc(root.Map) ? root.Map : function Map() {
Object.defineProperties(this, {
inputs: {
value: [],
enumerable: false
},
outputs: {
value: [],
enumerable: false
}
});
};
// Retrieve the object that's mapped to `input`, or null if input is not
// found within the transfer map.
TransferMap.prototype.get = function(input) {
var idx = this.inputs.indexOf(input);
var output = null;
// All we need are the `get` and `set` public-facing methods so we shim just
// them.
if (idx > -1) {
output = this.outputs[idx];
}
if (!_isFunc(Map.prototype.set)) {
// Map a given `input` object to a given `output` object. Relatively
// straightforward.
Map.prototype.set = function(input, output) {
var inputIdx = this.inputs.indexOf(input);
if (inputIdx === -1) {
this.inputs.push(input);
this.outputs.push(output);
} else {
// Associate this input with the new output.
this.outputs[inputIdx] = output;
}
};
}
return output;
};
if (!_isFunc(Map.prototype.get)) {
// Retrieve the object that's mapped to `input`, or null if input is not
// found within the transfer map.
Map.prototype.get = function(input) {
var idx = this.inputs.indexOf(input);
var output = null;
// Regex used to test whether or not an object could be an HTML Element.
var _htmlElementRE = /^\[object\sHTML(.*?)Element\]$/;
if (idx > -1) {
output = this.outputs[idx];
}
return output;
};
}
// Any custom cloning procedures defined by the client will be stored here.
var _customCloneProcedures = [];
// Performs the "internal structured clone" portion of the structured cloning
// algorithm. `input` is any valid object, and `tMap` is a(n empty)
// TransferMap instance.
function _iSClone(input, tMap) {
// algorithm. `input` is any valid object, and `mMap` is a(n empty)
// Map instance. `options` is the same as it is for `clone`
function _iSClone(input, mMap, options) {
if (input === null) {

@@ -77,15 +113,16 @@ return null;

if (typeof input === 'object') {
return _handleObjectClone(input, tMap);
if (Object(input) === input) {
return _handleObjectClone(input, mMap, options);
}
// If the value is a primitive, simply return it.
return input;
}
// Here lies the meat and potatoes of the algorithm. _handleObjectClone
// is responsible for creating deep copies of complex data. Its parameters
// are the same as for _isClone.
function _handleObjectClone(input, tMap) {
// Here lies the meat and potatoes of the algorithm. `_handleObjectClone`
// is responsible for creating deep copies of complex objects. Its parameters
// are the same as for `_isClone`.
function _handleObjectClone(input, mMap, options) {
// First we make sure that we aren't dealing with a circular reference.
var _selfRef = tMap.get(input);
var _selfRef = mMap.get(input);
if (_selfRef !== null) {

@@ -95,3 +132,11 @@ return _selfRef;

// Most supported object types can be copied just be creating a new
// We also check up front to make sure that a client-defined custom
// procedure has not been registered for this type of object. If it has,
// it takes priority over any of the implementations below.
var _cloneAttempt = _attemptCustomClone(input);
if (typeof _cloneAttempt !== 'undefined') {
return _cloneAttempt;
}
// Most supported object types can be copied simply by creating a new
// instance of the object using its current value, so we save that in this

@@ -102,5 +147,5 @@ // variable.

var output;
// We defined a collection as either an array of Object other than String,
// Number, Boolean, Date, or RegExp objects. Basically any structure where
// recursive cloning may be necessary.
// We define a collection as either an array of Objects other than String,
// Number, Boolean, Date, or RegExp objects. Basically it's any structure
// where recursive cloning may be necessary.
var isCollection = false;

@@ -110,5 +155,5 @@

// These cases follow the W3C's specification for how certain objects
// are handled. Note that jshint will complain about using Object wrappers
// for primitives (as it should), but we have to handle this case should
// the client pass one in.
// are handled. Note that jshint will complain about using Object
// wrappers for primitives (as it should), but we have to handle this
// case should the client pass one in.

@@ -151,9 +196,7 @@ /*jshint -W053 */

default:
// If it's an HTML Element, try to clone it.
if (_htmlElementRE.test(obType) &&
typeof input.cloneNode === 'function') {
output = input.cloneNode();
// If `options.allowFunctions` is set to true, we allow functions to
// be passed directly into the copied object.
if (_isFunc(input) && (options.allowFunctions === true)) {
output = input;
} else {
// Otherwise just throw an error.
throw new TypeError(

@@ -163,9 +206,10 @@ "Don't know how to clone object of type " + obType

}
break;
}
// Map this specific object to its output in case its cyclically referenced
tMap.set(input, output);
mMap.set(input, output);
if (isCollection) {
_handleCollectionClone(input, output, tMap);
_handleCollectionClone(input, output, mMap, options);
}

@@ -177,3 +221,3 @@

// Handles the safe cloning of RegExp objects, where we explicitly pass the
// regex object the source and flags separately, as this prevents bugs
// regex object, the source, and flags separately, as this prevents bugs
// within phantomJS (and possibly other environments as well).

@@ -196,23 +240,86 @@ function _handleRegExpClone(re) {

// Handles the recursive portion of structured cloning.
function _handleCollectionClone(input, output, tMap) {
var prop;
function _handleCollectionClone(input, output, mMap, options) {
// Note that we use own property names here since we've already
// used `Object.create()` to create the duplicate, so we have
// already acquired the original object's prototype. Note that the W3C
// spec explicitly states that this algorithm does *not* walk the
// prototype chain, and therefore all Object prototypes are live
// (assigned as a reference).
Object.getOwnPropertyNames(input).forEach(function(prop) {
var desc = Object.getOwnPropertyDescriptor(input, prop);
// We only clone if the property is a non-accessor. We can't really clone
// getters and setters, we can only pass them through.
if (desc.value !== undefined) {
desc.value = _iSClone(desc.value, mMap, options);
}
for (prop in input) {
// Note that we use the hasOwnProperty guard here since we've already
// used either Object.create() to create the duplicate, so we have
// already acquired the original object's prototype. Note that the W3C
// spec explicitly states that this algorithm does *not* walk the
// prototype chain, and therefore all Object prototypes are live
// (assigned as a reference).
if (_hasProp(input, prop)) {
output[prop] = _iSClone(input[prop], tMap);
Object.defineProperty(output, prop, desc);
});
}
function _attemptCustomClone(obj) {
var proc;
var copy;
var procIdx = _customCloneProcedures.length;
// Note that if two procedures passed in detect the same type of object,
// the latest procedure will take priority.
while (procIdx--) {
proc = _customCloneProcedures[procIdx];
if (proc.detect(obj)) {
copy = proc.copy(obj);
break;
}
}
return copy;
}
// This is the module that we expose to the rest of the world, with one
// singular method. CY.clone...get it? :)
// This is the module that we expose to the rest of the world.
// CY.clone...get it? :)
var CY = {
clone: function(input) {
return _iSClone(input, new TransferMap());
clone: function(input, options) {
var result, map = new Map();
options = _mergeParams(((typeof options === 'object') ? options : {}), {
// If set to true, this will simply pass a function through to the
// copied object instead of throwing.
allowFunctions: false,
// If set to true, this will stop CY.clone() from throwing *any* errors
// at all if it can't clone the object. Instead, it will simply return
// `null`. This is useful if you don't want a bad clone to halt program
// execution.
suppressErrors: false
});
// Don't enter try/catch unless suppressErrors is given.
// We want to try to avoid context switches if we can to get the most
// performance possible out of this function.
if (options.suppressErrors === true) {
try {
result = _iSClone(input, map, options);
} catch (err) {
result = null;
} finally {
return result;
}
}
return _iSClone(input, map, options);
},
// Returns true if the procedure is successfullly defined, false otherwise.
defineCloneProcedure: function(procObj) {
// Make sure we can use this procedure
if (typeof procObj === 'object' &&
_isFunc(procObj.detect) &&
_isFunc(procObj.copy)) {
_customCloneProcedures.push(procObj);
return true;
}
return false;
},
clearCustomCloneProcedures: function() {
_customCloneProcedures = [];
}

@@ -226,3 +333,3 @@ };

module.exports = CY;
} else if (typeof define === 'function' && typeof require === 'function') {
} else if (_isFunc(define) && _isFunc(require)) {
// AMD/RequireJS

@@ -229,0 +336,0 @@ define([], function() { return CY; });

{
"name": "cyclonejs",
"version": "0.0.2",
"version": "1.0.0",
"description": "A pure-javascript adaptation of the W3C's structured cloning algorithm, designed to provide an easy interface for deep copying of complex objects",
"main": "cyclone.js",
"scripts": {
"test": "./node_modules/.bin/jshint *.js test/*.js && ./node_modules/.bin/mocha --reporter spec test/cyclone.mspec.js"
"pretest": "./node_modules/.bin/jshint *.js test/*.js",
"test": "./node_modules/.bin/mocha --reporter spec test/*.mspec.js"
},

@@ -25,3 +26,4 @@ "repository": {

"expect.js": "~0.2.0",
"jshint": "~2.1.4"
"jshint": "~2.1.4",
"docco": "~0.6.2"
},

@@ -28,0 +30,0 @@ "testling": {

[![Build Status](https://travis-ci.org/traviskaufman/cycloneJS.png)](https://travis-ci.org/traviskaufman/cycloneJS)
[![browser support](https://ci.testling.com/traviskaufman/cycloneJS.png)](https://ci.testling.com/traviskaufman/cycloneJS)

@@ -19,3 +18,2 @@ Cyclone is an attempt to implement an adaptation of the HTML5 [Structured

See above about serving the *majority* of use cases.
* Able to copy DOM objects via `cloneNode`

@@ -37,3 +35,4 @@ It can handle objects containing:

* Cyclic references to objects within itself, including nested cyclic references to those objects
* DOM Objects
* Non-enumerable properties, or properties with custom descriptors
* Accessor properties

@@ -63,3 +62,3 @@ ## Usage

Note that cycloneJS also supports AMD loading as well as use within nodeJS, so with node you could do something like
Note that cycloneJS also supports AMD loading as well as use within nodeJS/CJS environments, so with node you could do something like
```javascript

@@ -70,2 +69,72 @@ var CY = require('cyclonejs');

### Cloning options
`CY.clone` takes an options hash as a second argument, which accepts the following parameters:
* `allowFunctions`: (default: `false`) If set to true, `CY.clone` will simply pass functions through to the copied object, instead of throwing an error saying it can't clone a function.
```javascript
var fnObject = {
f: function() {}
};
var copy = CY.clone(fnObject, {
allowFunctions: true
});
console.log(copy.f === fnObject.f) // true
```
* `suppressErrors`: (default: `false`) If set to true, `CY.clone` will return `null` instead of throwing an Error if it comes across an object it doesn't know how to clone. If you need `CY.clone` to be extremely forgiving, this is the option for you.
```javascript
var mixedBag = {
htmlElement: document.createElement('div'),
ctx: document.getElementsByTagName('canvas')[0].getContext('2d')
};
var result = CY.clone(mixedBag, {
suppressErrors: true
});
console.log(result); // null
```
### Extending `CY.clone()`'s functionality with `defineCloneProcedure`
Because cyclone is built to be environment-agnostic, it is incapable of handling certain cloneable host objects, such as DOM elements, out-of-the-box. However, that doesn't mean that these objects themselves aren't cloneable! For example, most DOM elements can be cloned using `cloneNode()`. Cyclone comes with a method called `defineCloneProcedure` that allows you to add in your own cloning methods for host objects, or other objects, that can't be handled in a standard way by `CY.clone`. Here's how you can add support for cloning DOM nodes using `CY.clone`:
```javascript
var elementTagRE = /^HTML\w*Element$/;
CY.defineCloneProcedure({
detect: function(obj) {
var klass = Object.prototype.toString.call(obj).slice(8, -1);
return elementTagRE.test(klass) && typeof obj.cloneNode === 'function';
},
copy: function(el) {
return el.cloneNode()
}
});
var orig = document.createElement('div');
var clone = CY.clone(orig);
console.log(clone !== orig); // True
```
Here's another example of how to handle most jQuery objects:
```javascript
CY.defineCloneProcedure({
detect: function(obj) {
return 'jquery' in obj;
},
copy: function($el) {
return $el.clone();
}
});
CY.clone($('#main')); // Returns a cloned jQuery object.
```
`defineCloneProcedure` takes an object that _must_ contain two properties `detect` and `copy`.
`detect` should be a function that takes an object and returns `true` if and only if `obj` is of the type that you want to define your custom cloning procedure for. In the above example, `detect` ensures that the `[[Class]]` of `obj` is specified as some kind of HTML Element, and that it has a function called `cloneNode`.
`copy` should be a function that takes an object and returns a copy of that object. You are responsible for providing the procedure used to copy the object. In the case of an HTML Element, we simply call its `cloneNode` method.
`defineCloneProcedure` will return `true` if the cloning procedure is successfully defined, and `false` otherwise. Note that `defineCloneProcedure` gives priority to procedures that were defined most recently. That means if you define two cloning procedures whose `detect` functions both return true for a given type of object, the latter's `copy` function will be used.
You can erase all custom cloning procedures defined by calling `CY.clearCustomCloneProcedures()`.
## Contributing/Testing

@@ -81,5 +150,2 @@ First install the module

## Coming Soon
* Ability to define your own protocols for copying unsupported and/or custom
objects.
* Features other people contribute.
Issues and Pull Requests are widely encouraged!!

@@ -9,9 +9,2 @@ /**

var original;
var number = Math.random();
var regex = /^someRE$/g;
var bool = false;
var string = 'hey';
var date = new Date();
var array = [1, 2, 3];
var object = {};

@@ -24,10 +17,10 @@ // http://stackoverflow.com/questions/10776600/

if (r1 instanceof RegExp && r2 instanceof RegExp) {
props = ["global", "multiline", "ignoreCase", "source"];
for (var i = 0; i < props.length; i++) {
prop = props[i];
if (r1[prop] !== r2[prop]) {
return false;
}
props = ['global', 'multiline', 'ignoreCase', 'source'];
for (var i = 0; i < props.length; i++) {
prop = props[i];
if (r1[prop] !== r2[prop]) {
return false;
}
return true;
}
return true;
}

@@ -38,2 +31,3 @@ return false;

beforeEach(function() {
var val = 0;
/*jshint -W053 */

@@ -58,2 +52,16 @@ original = {

};
Object.defineProperty(original, 'nonEnumerable', {
value: 'sup',
enumerable: false,
writable: true,
configurable: true
});
Object.defineProperty(original, 'accessor', {
get: function() { return val; },
set: function(v) { val = v; },
configurable: true,
enumerable: true
});
});

@@ -91,3 +99,5 @@

it('clones number objects', function() {
expect(_isClonedWrapper(original.numberObj, clone.numberObj)).to.be(true);
expect(
_isClonedWrapper(original.numberObj, clone.numberObj)
).to.be(true);
});

@@ -105,3 +115,5 @@

it('clones string objects', function() {
expect(_isClonedWrapper(original.stringObj, clone.stringObj)).to.be(true);
expect(
_isClonedWrapper(original.stringObj, clone.stringObj)
).to.be(true);
});

@@ -139,5 +151,15 @@ });

// WE CAN GO DEEPER!
expect(original.object.nested.array).not.to.be(clone.object.nested.array);
expect(original.object.nested.array).not.to.be(
clone.object.nested.array
);
expect(original.object.nested.array).to.eql(clone.object.nested.array);
});
it("throws an error if it doesn't know how to clone an object",
function() {
original.f = function() {};
expect(function() {
CY.clone(original);
}).to.throwError();
});
});

@@ -164,4 +186,19 @@ }); // Basic Functionality

// Not yet implemented
xdescribe('Custom cloning procedures', function() {
describe('ES5 Considerations', function() {
it('clones non-enumerable properties', function() {
expect(clone.nonEnumerable).to.be(original.nonEnumerable);
});
it('preserves descriptor values on copied properties', function() {
expect(
Object.getOwnPropertyDescriptor(clone, 'nonEnumerable').enumerable
).to.be(false);
});
it('can handle accessor properties', function() {
expect(clone.accessor).to.be(0);
});
});
describe('Custom cloning procedures', function() {
function Person(name) {

@@ -173,6 +210,7 @@ this.name = name;

};
var eminem = 'The Real Shady';
var eminem;
var theRealSlimShady;
beforeEach(function() {
eminem = 'The Real Shady';
theRealSlimShady = new Person(eminem);

@@ -190,3 +228,3 @@ });

},
copy: function(obj) {
copy: function() {
return new Person('Otha Slim Shady');

@@ -200,6 +238,57 @@ }

});
it('gives precedence to procs defined at a later time', function() {
var detectFn = function(obj) {
return (obj.name === eminem);
};
CY.defineCloneProcedure({
detect: detectFn,
copy: function() {
return 'Kim';
}
});
CY.defineCloneProcedure({
detect: detectFn,
copy: function() {
return eminem;
}
});
expect(CY.clone(theRealSlimShady)).to.be(eminem);
});
it('returns true on successful definition', function() {
expect(CY.defineCloneProcedure({
detect: function(obj) { return obj === true; },
copy: function(obj) { return 'true'; }
})).to.be(true);
});
it('returns false on unsuccessful definition', function() {
expect(CY.defineCloneProcedure({})).to.be(false);
});
it("will fail if an object isn't passed in", function() {
expect(CY.defineCloneProcedure('herp')).to.be(false);
});
it('will fail if the object lacks a `detect` function', function() {
expect(CY.defineCloneProcedure({
detect: 'nope',
copy: function() {}
})).to.be(false);
});
it('will fail if the object lacks a `copy` function', function() {
expect(CY.defineCloneProcedure({
detect: function() {},
copy: 'nope'
})).to.be(false);
});
});
describe('edge cases', function() {
it('returns the value of a primitive if it is explicitly passed one', function() {
it('returns the value of a primitive if explicitly passed one', function() {
expect(CY.clone(1)).to.be(1);

@@ -218,2 +307,30 @@ expect(CY.clone('hey')).to.be('hey');

});
describe('options for CY.clone', function() {
it('has an `allowFunctions` option that will pass functions ' +
'through', function() {
var copy;
original.f = function() {};
copy = CY.clone(original, {
allowFunctions: true
});
expect(original.f).to.be(copy.f);
});
it('has a `suppressErrors` option that will return null on bad clone ' +
'instead of throwing an error', function() {
original.f = function() {};
expect(function() {
CY.clone(original, {
suppressErrors: true
});
}).to.not.throwException();
});
it('returns null if `suppressErrors` is true and an error is thrown', function() {
original.f = function() {};
expect(CY.clone(original, {suppressErrors: true})).to.be(null);
});
});
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc