mirror of
				https://github.com/flynx/features.js.git
				synced 2025-10-31 03:10:09 +00:00 
			
		
		
		
	moved to new feature inertization algorithm, still testing...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									2754cf5457
								
							
						
					
					
						commit
						6f553d2408
					
				
							
								
								
									
										353
									
								
								features.js
									
									
									
									
									
								
							
							
						
						
									
										353
									
								
								features.js
									
									
									
									
									
								
							| @ -170,6 +170,8 @@ module.FeatureProto = { | |||||||
| // 	Feature(tag, obj)
 | // 	Feature(tag, obj)
 | ||||||
| // 		-> feature
 | // 		-> feature
 | ||||||
| //
 | //
 | ||||||
|  | // 	Feature(tag, suggested)
 | ||||||
|  | // 		-> feature
 | ||||||
| //
 | //
 | ||||||
| // 	Feature(tag, actions)
 | // 	Feature(tag, actions)
 | ||||||
| // 		-> feature
 | // 		-> feature
 | ||||||
| @ -337,6 +339,7 @@ var FeatureSetProto = { | |||||||
| 	// XXX add dependency loops to .conflicts...
 | 	// XXX add dependency loops to .conflicts...
 | ||||||
| 	// XXX might be a good idea to check dependency loops on feature 
 | 	// XXX might be a good idea to check dependency loops on feature 
 | ||||||
| 	// 		construction, too... (???)
 | 	// 		construction, too... (???)
 | ||||||
|  | 	/* | ||||||
| 	buildFeatureList: function(obj, lst){ | 	buildFeatureList: function(obj, lst){ | ||||||
| 		var that = this | 		var that = this | ||||||
| 		obj = obj || {} | 		obj = obj || {} | ||||||
| @ -666,8 +669,139 @@ var FeatureSetProto = { | |||||||
| 			conflicts: conflicts, | 			conflicts: conflicts, | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
|  | 	setup: function(obj, lst){ | ||||||
|  | 		// if no explicit object is given, just the list...
 | ||||||
|  | 		if(lst == null){ | ||||||
|  | 			lst = obj | ||||||
|  | 			obj = null | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 	// Build list of features in an appropriate order to load...
 | 		obj = obj || (this.__actions__ || actions.Actions)() | ||||||
|  | 
 | ||||||
|  | 		lst = lst.constructor !== Array ? [lst] : lst | ||||||
|  | 		var features = this.buildFeatureList(obj, lst) | ||||||
|  | 		lst = features.features | ||||||
|  | 
 | ||||||
|  | 		// check for conflicts...
 | ||||||
|  | 		if(Object.keys(features.conflicts).length != 0 | ||||||
|  | 				|| Object.keys(features.missing).length != 0){ | ||||||
|  | 			var m = features.missing | ||||||
|  | 			var c = features.conflicts | ||||||
|  | 
 | ||||||
|  | 			// build a report...
 | ||||||
|  | 			var report = [] | ||||||
|  | 
 | ||||||
|  | 			// missing deps...
 | ||||||
|  | 			Object.keys(m).forEach(function(k){ | ||||||
|  | 				report.push(k + ': requires following missing features:\n' | ||||||
|  | 					+'          ' + m[k].join(', ')) | ||||||
|  | 			}) | ||||||
|  | 			report.push('\n') | ||||||
|  | 
 | ||||||
|  | 			// conflicts...
 | ||||||
|  | 			Object.keys(c).forEach(function(k){ | ||||||
|  | 				report.push(k + ': must setup after:\n          ' + c[k].join(', ')) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			// break...
 | ||||||
|  | 			throw 'Feature dependency error:\n    ' + report.join('\n    ')  | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// report excluded features...
 | ||||||
|  | 		if(this.__verbose__ && features.excluded.length > 0){ | ||||||
|  | 			console.warn('Excluded features due to exclusivity conflict:',  | ||||||
|  | 					features.excluded.join(', ')) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// report unapplicable features...
 | ||||||
|  | 		if(this.__verbose__ && features.unapplicable.length > 0){ | ||||||
|  | 			console.log('Features not applicable in current context:',  | ||||||
|  | 					features.unapplicable.join(', ')) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// do the setup...
 | ||||||
|  | 		var that = this | ||||||
|  | 		var setup = FeatureProto.setup | ||||||
|  | 		lst.forEach(function(n){ | ||||||
|  | 			// setup...
 | ||||||
|  | 			if(that[n] != null){ | ||||||
|  | 				this.__verbose__ && console.log('Setting up feature:', n) | ||||||
|  | 				setup.call(that[n], obj) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		// XXX should we extend this if it already was in the object???
 | ||||||
|  | 		obj.features = features | ||||||
|  | 
 | ||||||
|  | 		return obj | ||||||
|  | 	}, | ||||||
|  | 	//*/
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	// Build list of features in load order...
 | ||||||
|  | 	//
 | ||||||
|  | 	// 	.buildFeatureList()
 | ||||||
|  | 	// 	.buildFeatureList('*')
 | ||||||
|  | 	// 		-> data
 | ||||||
|  | 	//
 | ||||||
|  | 	// 	.buildFeatureList(feature-tag)
 | ||||||
|  | 	// 		-> data
 | ||||||
|  | 	//
 | ||||||
|  | 	// 	.buildFeatureList([feature-tag, .. ])
 | ||||||
|  | 	// 		-> data
 | ||||||
|  | 	//
 | ||||||
|  | 	// 	.buildFeatureList(.., filter)
 | ||||||
|  | 	// 		-> data
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	// return format:
 | ||||||
|  | 	// 	{
 | ||||||
|  | 	//		// input feature feature tags...
 | ||||||
|  | 	//		input: [ .. ],
 | ||||||
|  | 	//
 | ||||||
|  | 	//		// output list of feature tags...
 | ||||||
|  | 	//		features: [ .. ],
 | ||||||
|  | 	//
 | ||||||
|  | 	//		// disabled features...
 | ||||||
|  | 	//		disabled: [ .. ],
 | ||||||
|  | 	//		// exclusive features that got excluded... 
 | ||||||
|  | 	//		excluded: [ .. ],
 | ||||||
|  | 	//
 | ||||||
|  | 	//		// Errors...
 | ||||||
|  | 	//		error: null | {
 | ||||||
|  | 	//			// fatal/recoverable error indicator...
 | ||||||
|  | 	//			fatal: bool,
 | ||||||
|  | 	//
 | ||||||
|  | 	//			// missing dependencies...
 | ||||||
|  | 	//			// NOTE: this includes tags only included by .depends and 
 | ||||||
|  | 	//			//		ignores tags from .suggested...
 | ||||||
|  | 	//			missing: [ .. ],
 | ||||||
|  | 	//
 | ||||||
|  | 	//			// XXX
 | ||||||
|  | 	//			conflicts: conflicts,
 | ||||||
|  | 	//
 | ||||||
|  | 	//			// detected dependency loops (if .length > 0 sets fatal)...
 | ||||||
|  | 	//			loops: [ .. ],
 | ||||||
|  | 	//
 | ||||||
|  | 	//			// sorting loop overflow error (if true sets fatal)...
 | ||||||
|  | 	//			sort_loop_overflow: bool,
 | ||||||
|  | 	//		},
 | ||||||
|  | 	//
 | ||||||
|  | 	//		// Introspection...
 | ||||||
|  | 	//		// index of features and their list of dependencies...
 | ||||||
|  | 	//		depends: {
 | ||||||
|  | 	//			feature-tag: [ feature-tag, .. ],
 | ||||||
|  | 	//			..
 | ||||||
|  | 	//		},
 | ||||||
|  | 	//		// index of features and list of features depending on them...
 | ||||||
|  | 	//		// XXX should this include suggestions or should we do them 
 | ||||||
|  | 	//		//		in a separate list...
 | ||||||
|  | 	//		depended: { 
 | ||||||
|  | 	//			feature-tag: [ feature-tag, .. ],
 | ||||||
|  | 	//			..
 | ||||||
|  | 	//		},
 | ||||||
|  | 	// 	}
 | ||||||
|  | 	//
 | ||||||
| 	//
 | 	//
 | ||||||
| 	// Algorithm:
 | 	// Algorithm:
 | ||||||
| 	// 	- expand features:
 | 	// 	- expand features:
 | ||||||
| @ -679,11 +813,12 @@ var FeatureSetProto = { | |||||||
| 	// 		- by priority
 | 	// 		- by priority
 | ||||||
| 	// 		- by dependency (detect loops/errors)
 | 	// 		- by dependency (detect loops/errors)
 | ||||||
| 	//
 | 	//
 | ||||||
|  | 	//
 | ||||||
| 	// XXX differences to .buildFeatureList(..):
 | 	// XXX differences to .buildFeatureList(..):
 | ||||||
| 	// 		- order is slightly different -- within expectations...
 | 	// 		- order is slightly different -- within expectations...
 | ||||||
| 	// 		- this includes meta-features...
 | 	// 		- this includes meta-features...
 | ||||||
| 	// 			this seems to be more logical and more flexible...
 | 	// 			this seems to be more logical and more flexible...
 | ||||||
| 	_buildFeatureList: function(lst, filter){ | 	buildFeatureList: function(lst, filter){ | ||||||
| 		var all = this.features | 		var all = this.features | ||||||
| 		lst = (lst == null || lst == '*') ? all : lst | 		lst = (lst == null || lst == '*') ? all : lst | ||||||
| 		lst = lst.constructor !== Array ? [lst] : lst | 		lst = lst.constructor !== Array ? [lst] : lst | ||||||
| @ -1086,41 +1221,31 @@ var FeatureSetProto = { | |||||||
| 
 | 
 | ||||||
| 		//-------------------------------------------------------------
 | 		//-------------------------------------------------------------
 | ||||||
| 		 | 		 | ||||||
| 		// XXX should we report stuff here???
 |  | ||||||
| 		// report dependency loops...
 |  | ||||||
| 		//
 |  | ||||||
| 		// NOTE: a loop error should be raised only when one of the loop elements
 |  | ||||||
| 		// 		is encountered during the linearisation process...
 |  | ||||||
| 		// XXX should looping features get disabled or be loaded in random-ish order???
 |  | ||||||
| 		// 		...#2 case will need to be handled at the sorting stage...
 |  | ||||||
| 		loops.length > 0 |  | ||||||
| 			&& loops |  | ||||||
| 				.forEach(function(loop){ |  | ||||||
| 					console.warn('feature loop detected:\n\t' + loop.join('\n\t\t-> ')) }) |  | ||||||
| 		// report conflicts...
 |  | ||||||
| 		Object.keys(conflicts) |  | ||||||
| 			.forEach(function(group){ |  | ||||||
| 				console.error('Exclusive "'+ group +'" conflict at:', conflicts[group]) }) |  | ||||||
| 		// report loop limit...
 |  | ||||||
| 		// XXX store this to data...
 |  | ||||||
| 		loop_limit <= 0 |  | ||||||
| 			&& console.error('Hit loop limit while sorting dependencies!') |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 		return { | 		return { | ||||||
| 			input: lst, | 			input: lst, | ||||||
| 
 | 
 | ||||||
| 			list: list, | 			features: list, | ||||||
| 
 | 
 | ||||||
| 			disabled: disabled, | 			disabled: disabled, | ||||||
| 			excluded: excluded, | 			excluded: excluded, | ||||||
| 
 | 
 | ||||||
| 			// XXX should these be in a error block???
 | 			// errors and conflicts...
 | ||||||
| 			missing: missing, | 			error: (loops.length > 0  | ||||||
| 			loops: loops, | 					|| Object.keys(conflicts).length > 0  | ||||||
| 			conflicts: conflicts, | 					|| loop_limit <= 0  | ||||||
| 			sort_loop_error: loop_limit <= 0, | 					|| missing.length > 0) ? | ||||||
|  | 				{ | ||||||
|  | 					missing: missing, | ||||||
|  | 					conflicts: conflicts, | ||||||
| 
 | 
 | ||||||
|  | 					// fatal stuff...
 | ||||||
|  | 					fatal: loops.length > 0 || loop_limit <= 0, | ||||||
|  | 					loops: loops, | ||||||
|  | 					sort_loop_overflow: loop_limit <= 0, | ||||||
|  | 				} | ||||||
|  | 				: null, | ||||||
|  | 
 | ||||||
|  | 			// introspection...
 | ||||||
| 			depends: features, | 			depends: features, | ||||||
| 			depended: rev_features, | 			depended: rev_features, | ||||||
| 			//suggests: suggested,
 | 			//suggests: suggested,
 | ||||||
| @ -1128,18 +1253,34 @@ var FeatureSetProto = { | |||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	_setup: function(obj, lst){ | 	// Setup features...
 | ||||||
| 		// if no explicit object is given, just the list...
 | 	//
 | ||||||
|  | 	//	Setup features on existing actions object...
 | ||||||
|  | 	//	.setup(actions, [feature-tag, ...])
 | ||||||
|  | 	//		-> actions
 | ||||||
|  | 	//
 | ||||||
|  | 	//	Setup features on a new actions object...
 | ||||||
|  | 	//	.setup(feature-tag)
 | ||||||
|  | 	//	.setup([feature-tag, ...])
 | ||||||
|  | 	//		-> actions
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	// This will add .unapplicable to the output of .buildFeatureList(..) 
 | ||||||
|  | 	// and to .features of the resulting object...
 | ||||||
|  | 	//
 | ||||||
|  | 	// NOTE: this will store the build result in .features of the output 
 | ||||||
|  | 	// 		actions object.
 | ||||||
|  | 	setup: function(obj, lst){ | ||||||
|  | 		// no explicit object is given...
 | ||||||
| 		if(lst == null){ | 		if(lst == null){ | ||||||
| 			lst = obj | 			lst = obj | ||||||
| 			obj = null | 			obj = null | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 		obj = obj || (this.__actions__ || actions.Actions)() | 		obj = obj || (this.__actions__ || actions.Actions)() | ||||||
|  | 		lst = lst instanceof Array ? lst : [lst] | ||||||
| 
 | 
 | ||||||
| 		lst = lst.constructor !== Array ? [lst] : lst |  | ||||||
| 		var unapplicable = [] | 		var unapplicable = [] | ||||||
| 		var features = this._buildFeatureList(lst,  | 		var features = this.buildFeatureList(lst,  | ||||||
| 			function(n){ | 			function(n){ | ||||||
| 				var f = this[n] | 				var f = this[n] | ||||||
| 				// check applicability if possible...
 | 				// check applicability if possible...
 | ||||||
| @ -1149,55 +1290,44 @@ var FeatureSetProto = { | |||||||
| 				} | 				} | ||||||
| 				return true | 				return true | ||||||
| 			})  | 			})  | ||||||
| 		lst = features.list |  | ||||||
| 
 |  | ||||||
| 		// XXX STUB: adapter code, remove when done...
 |  | ||||||
| 		features.unapplicable = unapplicable  | 		features.unapplicable = unapplicable  | ||||||
| 		features.features = features.list | 		// cleanup disabled...
 | ||||||
|  | 		features.disabled = features.disabled | ||||||
|  | 			.filter(function(n){ return unapplicable.indexOf(n) < 0 }) | ||||||
| 
 | 
 | ||||||
| 		// check for conflicts...
 | 		// if we have critical errors and set verbose...
 | ||||||
| 		/*/ XXX need to update this section... | 		var fatal = features.error  | ||||||
| 		if(Object.keys(features.conflicts).length != 0 | 			&& (features.error.loops.length > 0 || features.error.sort_loop_overflow) | ||||||
| 				|| Object.keys(features.missing).length != 0){ | 		var verbose = this.__verbose__ || fatal  | ||||||
| 			var m = features.missing |  | ||||||
| 			var c = features.conflicts |  | ||||||
| 
 | 
 | ||||||
| 			// build a report...
 | 		// report stuff...
 | ||||||
| 			var report = [] | 		if(verbose){ | ||||||
| 
 | 			// report dependency loops...
 | ||||||
| 			// missing deps...
 | 			features.error.loops.length > 0 | ||||||
| 			Object.keys(m).forEach(function(k){ | 				&& loops | ||||||
| 				report.push(k + ': requires following missing features:\n' | 					.forEach(function(loop){ | ||||||
| 					+'          ' + m[k].join(', ')) | 						console.warn('feature loop detected:\n\t' + loop.join('\n\t\t-> ')) }) | ||||||
| 			}) | 			// report conflicts...
 | ||||||
| 			report.push('\n') | 			Object.keys(features.error.conflicts) | ||||||
| 
 | 				.forEach(function(group){ | ||||||
| 			// conflicts...
 | 					console.error('Exclusive "'+ group +'" conflict at:', conflicts[group]) }) | ||||||
| 			Object.keys(c).forEach(function(k){ | 			// report loop limit...
 | ||||||
| 				report.push(k + ': must setup after:\n          ' + c[k].join(', ')) | 			features.error.sort_loop_overflow | ||||||
| 			}) | 				&& console.error('Hit loop limit while sorting dependencies!') | ||||||
| 
 |  | ||||||
| 			// break...
 |  | ||||||
| 			throw 'Feature dependency error:\n    ' + report.join('\n    ')  |  | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// report excluded features...
 | 		obj.features = features | ||||||
| 		if(this.__verbose__ && features.excluded.length > 0){ |  | ||||||
| 			console.warn('Excluded features due to exclusivity conflict:',  |  | ||||||
| 					features.excluded.join(', ')) |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		// report unapplicable features...
 | 		// fatal error -- can't load...
 | ||||||
| 		if(this.__verbose__ && features.unapplicable.length > 0){ | 		// XXX should we throw an error here???
 | ||||||
| 			console.log('Features not applicable in current context:',  | 		if(fatal){ | ||||||
| 					features.unapplicable.join(', ')) | 			return | ||||||
| 		} | 		} | ||||||
| 		//*/
 |  | ||||||
| 
 | 
 | ||||||
| 		// do the setup...
 | 		// do the setup...
 | ||||||
| 		var that = this | 		var that = this | ||||||
| 		var setup = FeatureProto.setup | 		var setup = FeatureProto.setup | ||||||
| 		lst.forEach(function(n){ | 		features.features.forEach(function(n){ | ||||||
| 			// setup...
 | 			// setup...
 | ||||||
| 			if(that[n] != null){ | 			if(that[n] != null){ | ||||||
| 				this.__verbose__ && console.log('Setting up feature:', n) | 				this.__verbose__ && console.log('Setting up feature:', n) | ||||||
| @ -1205,83 +1335,6 @@ var FeatureSetProto = { | |||||||
| 			} | 			} | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		// XXX should we extend this if it already was in the object???
 |  | ||||||
| 		obj.features = features |  | ||||||
| 
 |  | ||||||
| 		return obj |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	//
 |  | ||||||
| 	//	.setup(<actions>, [<feature>, ...])
 |  | ||||||
| 	//		-> <actions>
 |  | ||||||
| 	//
 |  | ||||||
| 	//	.setup([<feature>, ...])
 |  | ||||||
| 	//		-> <actions>
 |  | ||||||
| 	//
 |  | ||||||
| 	setup: function(obj, lst){ |  | ||||||
| 		// if no explicit object is given, just the list...
 |  | ||||||
| 		if(lst == null){ |  | ||||||
| 			lst = obj |  | ||||||
| 			obj = null |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		obj = obj || (this.__actions__ || actions.Actions)() |  | ||||||
| 
 |  | ||||||
| 		lst = lst.constructor !== Array ? [lst] : lst |  | ||||||
| 		var features = this.buildFeatureList(obj, lst) |  | ||||||
| 		lst = features.features |  | ||||||
| 
 |  | ||||||
| 		// check for conflicts...
 |  | ||||||
| 		if(Object.keys(features.conflicts).length != 0 |  | ||||||
| 				|| Object.keys(features.missing).length != 0){ |  | ||||||
| 			var m = features.missing |  | ||||||
| 			var c = features.conflicts |  | ||||||
| 
 |  | ||||||
| 			// build a report...
 |  | ||||||
| 			var report = [] |  | ||||||
| 
 |  | ||||||
| 			// missing deps...
 |  | ||||||
| 			Object.keys(m).forEach(function(k){ |  | ||||||
| 				report.push(k + ': requires following missing features:\n' |  | ||||||
| 					+'          ' + m[k].join(', ')) |  | ||||||
| 			}) |  | ||||||
| 			report.push('\n') |  | ||||||
| 
 |  | ||||||
| 			// conflicts...
 |  | ||||||
| 			Object.keys(c).forEach(function(k){ |  | ||||||
| 				report.push(k + ': must setup after:\n          ' + c[k].join(', ')) |  | ||||||
| 			}) |  | ||||||
| 
 |  | ||||||
| 			// break...
 |  | ||||||
| 			throw 'Feature dependency error:\n    ' + report.join('\n    ')  |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// report excluded features...
 |  | ||||||
| 		if(this.__verbose__ && features.excluded.length > 0){ |  | ||||||
| 			console.warn('Excluded features due to exclusivity conflict:',  |  | ||||||
| 					features.excluded.join(', ')) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// report unapplicable features...
 |  | ||||||
| 		if(this.__verbose__ && features.unapplicable.length > 0){ |  | ||||||
| 			console.log('Features not applicable in current context:',  |  | ||||||
| 					features.unapplicable.join(', ')) |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// do the setup...
 |  | ||||||
| 		var that = this |  | ||||||
| 		var setup = FeatureProto.setup |  | ||||||
| 		lst.forEach(function(n){ |  | ||||||
| 			// setup...
 |  | ||||||
| 			if(that[n] != null){ |  | ||||||
| 				this.__verbose__ && console.log('Setting up feature:', n) |  | ||||||
| 				setup.call(that[n], obj) |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 
 |  | ||||||
| 		// XXX should we extend this if it already was in the object???
 |  | ||||||
| 		obj.features = features |  | ||||||
| 
 |  | ||||||
| 		return obj | 		return obj | ||||||
| 	}, | 	}, | ||||||
| 	remove: function(obj, lst){ | 	remove: function(obj, lst){ | ||||||
| @ -1296,7 +1349,6 @@ var FeatureSetProto = { | |||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	// shorthand for: Feature(<feature-set>, ...)
 | 	// shorthand for: Feature(<feature-set>, ...)
 | ||||||
| 	// XXX should this return this?
 |  | ||||||
| 	Feature: function(){ | 	Feature: function(){ | ||||||
| 		return this.__feature__.apply(null, [this].concat(args2array(arguments))) }, | 		return this.__feature__.apply(null, [this].concat(args2array(arguments))) }, | ||||||
| } | } | ||||||
| @ -1306,7 +1358,6 @@ var FeatureSet = | |||||||
| module.FeatureSet = object.makeConstructor('FeatureSet', FeatureSetProto) | module.FeatureSet = object.makeConstructor('FeatureSet', FeatureSetProto) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| //---------------------------------------------------------------------
 | //---------------------------------------------------------------------
 | ||||||
| 
 | 
 | ||||||
| var Features = | var Features = | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "ig-features", |   "name": "ig-features", | ||||||
|   "version": "2.2.6", |   "version": "3.0.0", | ||||||
|   "description": "", |   "description": "", | ||||||
|   "main": "features.js", |   "main": "features.js", | ||||||
|   "scripts": { |   "scripts": { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user