mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-11-04 05:10:07 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			561 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			561 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
/**********************************************************************
 | 
						|
* 
 | 
						|
*
 | 
						|
*
 | 
						|
**********************************************************************/
 | 
						|
 | 
						|
define(function(require){ var module = {}
 | 
						|
 | 
						|
if(typeof(window) == 'undefined'){
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
//var DEBUG = DEBUG != null ? DEBUG : true
 | 
						|
 | 
						|
var object = require('lib/object')
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*********************************************************************/
 | 
						|
 | 
						|
// convert a transform string to an object...
 | 
						|
//
 | 
						|
// Format:
 | 
						|
// 	{
 | 
						|
// 		<func>: [<arg>, ...],
 | 
						|
// 		...
 | 
						|
// 	}
 | 
						|
//
 | 
						|
// NOTE: this does not care about the semantics of the format, just the 
 | 
						|
// 		general structure...
 | 
						|
var transform2obj = function(str){
 | 
						|
	var res = {}
 | 
						|
	str = str || ''
 | 
						|
	// parse the string...
 | 
						|
	str
 | 
						|
		// split functions...
 | 
						|
		.split(/(\w+\([^\)]*)\)/)
 | 
						|
		// remove empty strings...
 | 
						|
		.filter(function(e){ return e.trim().length > 0})
 | 
						|
		// split each function...
 | 
						|
		.map(function(e){ return e
 | 
						|
			// split args...
 | 
						|
			.split(/\s*[\(,\s]\s*/)
 | 
						|
			// cleanup...
 | 
						|
			.filter(function(e){ return e.trim().length > 0 }) })
 | 
						|
		// build the structure...
 | 
						|
		.forEach(function(data){
 | 
						|
			var func = data.shift() 
 | 
						|
			var args = data
 | 
						|
 | 
						|
			res[func] = data
 | 
						|
		})
 | 
						|
	return res
 | 
						|
}
 | 
						|
 | 
						|
// Convert the object similar in structure to the produced by 
 | 
						|
// transform2obj(..) to a transform string...
 | 
						|
//
 | 
						|
// NOTE: this does not care about the actual semantics of the format, 
 | 
						|
// 		e.g. argument units or function names...
 | 
						|
var obj2transform = function(obj, filter){
 | 
						|
	// build the filters...
 | 
						|
	filter = filter || []
 | 
						|
	var keep = filter
 | 
						|
		.filter(function(f){ return f[0] != '-' }) 
 | 
						|
	var remove = filter
 | 
						|
		.filter(function(f){ return f[0] == '-' })
 | 
						|
		.map(function(f){ return f.slice(1) })
 | 
						|
 | 
						|
	return Object.keys(obj)
 | 
						|
		// keep...
 | 
						|
		.filter(function(func){ 
 | 
						|
			return keep.length == 0 || keep.indexOf(func) >= 0 })
 | 
						|
		// remove...
 | 
						|
		.filter(function(func){ 
 | 
						|
			return remove.indexOf(func) < 0 })
 | 
						|
		.map(function(func){
 | 
						|
			return func +'('+ obj[func].join(', ') + ')'
 | 
						|
		})
 | 
						|
		.join(' ')
 | 
						|
} 
 | 
						|
 | 
						|
 | 
						|
// XXX BUG: passing '' to an alias will clear ALL the aliased functions...
 | 
						|
// 		...should clear only full matches...
 | 
						|
// XXX BUG: passing '' to a multi-arg function will clear the args but 
 | 
						|
// 		still keep the function...
 | 
						|
// XXX BUG: setting a single arg alias will return string results...
 | 
						|
// 			.x(123)		-> ['123px']	-> must be [123]
 | 
						|
// 			.x()		-> [123]
 | 
						|
// 			.translate3d(1,2,3)
 | 
						|
// 						-> [1, 2, 3]
 | 
						|
// 		NOTE: both set data correctly...
 | 
						|
// XXX add argument in place operations...
 | 
						|
// 		+=
 | 
						|
// 		-=
 | 
						|
// 		*=
 | 
						|
// 		/=
 | 
						|
// XXX move the grammar out of this...
 | 
						|
// XXX need:
 | 
						|
// 		- a way to minimize this, i.e. get only full and minimal functions...
 | 
						|
// 		- a way to get what was defined as-is...
 | 
						|
// XXX STUB: .simplify(..) should be rewritten and be configurable...
 | 
						|
// 		...preferable work on write -- set the alias that already esists
 | 
						|
// 		and ignore the rest...
 | 
						|
// XXX make aliases collect and merge data, e.g. asking for scale with 
 | 
						|
// 		scaleX and scaleY set should return the combination of the two 
 | 
						|
// 		results...
 | 
						|
// XXX not critical yet but will need to support for completeness...
 | 
						|
//		- transformStyle 
 | 
						|
//		- prespective 
 | 
						|
//		- prespectiveOrigin 
 | 
						|
//		- backfaceVisibility
 | 
						|
// XXX add transitions...
 | 
						|
// XXX add support for vendor tags...
 | 
						|
var transformEditor = function(){
 | 
						|
	var editor = {
 | 
						|
		// data set...
 | 
						|
		data: {},
 | 
						|
 | 
						|
		// function that directly edit the data...
 | 
						|
		__direct: {},
 | 
						|
 | 
						|
 | 
						|
		// methods...
 | 
						|
		// XXX generate this...
 | 
						|
		simplify: function(filter){
 | 
						|
			var data = this.data
 | 
						|
 | 
						|
			// scale...
 | 
						|
			if(data.scale 
 | 
						|
					&& data.scale[0] == 1 
 | 
						|
					&& data.scale[1] == 1){ delete data.scale }
 | 
						|
			if((data.scaleX||[1])[0] == 1){ delete data.scaleX }
 | 
						|
			if((data.scaleY||[1])[0] == 1){ delete data.scaleY }
 | 
						|
 | 
						|
			// translate...
 | 
						|
			if(data.translate 
 | 
						|
					&& data.translate.len == 2 
 | 
						|
					&& parseFloat(data.translate[0]) == 0 
 | 
						|
					&& parseFloat(data.translate[1]) == 0){ delete data.translate }
 | 
						|
			if(data.translate3d
 | 
						|
					&& data.translate3d.len == 3 
 | 
						|
					&& parseFloat(data.translate3d[0]) == 0 
 | 
						|
					&& parseFloat(data.translate3d[1]) == 1
 | 
						|
					&& parseFloat(data.translate3d[2]) == 1){ delete data.translate3d }
 | 
						|
			if(parseFloat((data.translateX||[1])[0]) == 0){ delete data.translateX }
 | 
						|
			if(parseFloat((data.translateY||[1])[0]) == 0){ delete data.translateY }
 | 
						|
			if(parseFloat((data.translateZ||[1])[0]) == 0){ delete data.translateZ }
 | 
						|
 | 
						|
			// XXX rotate...
 | 
						|
 | 
						|
			// XXX skew...
 | 
						|
 | 
						|
			return this.data
 | 
						|
		},
 | 
						|
 | 
						|
		toString: function(){ 
 | 
						|
			//return obj2transform(this.data, args2array(arguments)) 
 | 
						|
			var args = args2array(arguments)
 | 
						|
			return obj2transform(this.simplify(args), args) 
 | 
						|
		},
 | 
						|
		// NOTE: this will not build the alias data...
 | 
						|
		fromString: function(str){ 
 | 
						|
			this.data = transform2obj(str) 
 | 
						|
			return this
 | 
						|
		},
 | 
						|
		// XXX use vendor prefix...
 | 
						|
		toElem: function(elem){
 | 
						|
			var origin = this.data.origin || ''
 | 
						|
			var transform = this.toString('-origin')
 | 
						|
 | 
						|
			elem = elem instanceof jQuery ? elem.toArray() 
 | 
						|
				: elem instanceof Array ? elem
 | 
						|
				: [elem]
 | 
						|
 | 
						|
			var e
 | 
						|
			for(var i = 0; i < elem.length; i++){
 | 
						|
				e = elem[i]
 | 
						|
				e.style.transformOrigin = origin.join ? origin.join(' ') : origin
 | 
						|
				e.style.transform = transform
 | 
						|
			}
 | 
						|
			/*
 | 
						|
			elem.forEach(function(e){
 | 
						|
				e.style.transformOrigin = origin.join ? origin.join(' ') : origin
 | 
						|
				e.style.transform = transform
 | 
						|
			})
 | 
						|
			*/
 | 
						|
 | 
						|
			return this
 | 
						|
		},
 | 
						|
 | 
						|
		// get data by attr names...
 | 
						|
		get: function(){
 | 
						|
			var that = this
 | 
						|
			var attrs = arguments[0] instanceof Array ?
 | 
						|
				arguments[0] 
 | 
						|
				: args2array(arguments)
 | 
						|
			var res = {}
 | 
						|
 | 
						|
			attrs.forEach(function(a){
 | 
						|
				if(!(a in that)){
 | 
						|
					return
 | 
						|
				}
 | 
						|
				var v = that[a]()
 | 
						|
				v = v.length == 1 ? v.pop() 
 | 
						|
					: v.length == 0 ? undefined
 | 
						|
					: v
 | 
						|
				res[a] = v
 | 
						|
			})
 | 
						|
 | 
						|
			if(attrs.length == 1){
 | 
						|
				return res[attrs[0]]
 | 
						|
			}
 | 
						|
 | 
						|
			return res
 | 
						|
		},
 | 
						|
 | 
						|
		// XXX use vendor prefix...
 | 
						|
		__init__: function(str){
 | 
						|
			this.data = {}
 | 
						|
 | 
						|
			if(str != null){
 | 
						|
				if(str instanceof jQuery){
 | 
						|
					str = str[0]
 | 
						|
				}
 | 
						|
				if(str instanceof HTMLElement){
 | 
						|
					var origin = str.style.transformOrigin
 | 
						|
					origin = origin.length > 0 ? ' origin('+ origin +')' : ''
 | 
						|
 | 
						|
					str = str.style.transform + origin
 | 
						|
				}
 | 
						|
				this.fromString(str)
 | 
						|
			}
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	var func = function(name, args, neutral){
 | 
						|
		args = args || []
 | 
						|
		editor.__direct[name] = function(val){
 | 
						|
			var that = this
 | 
						|
			// set...
 | 
						|
			if(val != null && val != ''){
 | 
						|
				val = val instanceof Array ? val : [val]
 | 
						|
				var data = this.data[name] = this.data[name] || []
 | 
						|
				var res = []
 | 
						|
				// add units and general processing...
 | 
						|
				val.map(function(arg, i){
 | 
						|
					// special case, if an arg is undefined do not change it...
 | 
						|
					if(arg === undefined){
 | 
						|
						return 
 | 
						|
					}
 | 
						|
					var unit = args[i] || ''
 | 
						|
					data[i] = typeof(arg) == typeof(123) 
 | 
						|
							|| (typeof(arg) == typeof('str') 
 | 
						|
								&& /^[0-9\.]+$/.test(arg)) ? arg + unit
 | 
						|
						: arg == '' ? neutral + unit
 | 
						|
						: arg
 | 
						|
					res[i] = arg
 | 
						|
				})
 | 
						|
				return res
 | 
						|
 | 
						|
			// delete...
 | 
						|
			} else if(val == ''){
 | 
						|
				delete this.data[name]
 | 
						|
 | 
						|
			// get...
 | 
						|
			} else {
 | 
						|
				var res = (this.data[name] || [])
 | 
						|
					// remove default unit...
 | 
						|
					.map(function(arg, i){
 | 
						|
						var unit = args[i] || ''
 | 
						|
						return arg.slice(-unit.length) == unit 
 | 
						|
								|| /^[0-9\.]+$/.test(arg)?
 | 
						|
							parseFloat(arg)
 | 
						|
							: arg
 | 
						|
					})
 | 
						|
				return res
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	
 | 
						|
	var alias = function(spec, reduce, mode){
 | 
						|
		reduce = reduce || 'last'
 | 
						|
		// can be:
 | 
						|
		// 	'first'
 | 
						|
		// 	'last'
 | 
						|
		// 	'all'
 | 
						|
		mode = mode || 'first'
 | 
						|
		// alias runner...
 | 
						|
		var handler = function(alias, args){
 | 
						|
			var that = this
 | 
						|
			// we only care for the source argument and only it will get
 | 
						|
			// passed next...
 | 
						|
			// NOTE: this is the name of the called alias...
 | 
						|
			var arg = args[spec[alias]]
 | 
						|
 | 
						|
			var aliases = Object.keys(spec)
 | 
						|
 | 
						|
			var r = reduce == 'sum' ? function(a, b){ return a + b }
 | 
						|
				: reduce == 'mul' ? function(a, b){ return a * b }
 | 
						|
				: reduce == 'last' ? function(a, b){ return b != null ? b : a }
 | 
						|
				: reduce
 | 
						|
			var n = reduce == 'sum' ? 0
 | 
						|
				: reduce == 'mul' ? 1
 | 
						|
				: 0
 | 
						|
 | 
						|
			return aliases.map(function(k, j){
 | 
						|
					var i = spec[k]
 | 
						|
 | 
						|
					// get state...
 | 
						|
					if(args.length == 0){
 | 
						|
						var res = k in that.__direct ? 
 | 
						|
							that.__direct[k].call(that) 
 | 
						|
							: null
 | 
						|
						return res != null ? res[i] : res
 | 
						|
					}
 | 
						|
 | 
						|
					// prepare arguments...
 | 
						|
					var a = []
 | 
						|
					a[i] = mode == 'first' && j == 0 ? arg
 | 
						|
						: mode == 'last' && j == aliases.length - 1 ? arg
 | 
						|
						: reduce == 'sum' ? 0
 | 
						|
						: reduce == 'mul' ? 1
 | 
						|
						: arg
 | 
						|
 | 
						|
					// do the call...
 | 
						|
					var res = k in that.__direct ?
 | 
						|
						that.__direct[k].call(that, a) 
 | 
						|
						: null
 | 
						|
					return res != null ? res[i] : res
 | 
						|
				})
 | 
						|
				.filter(function(e){ return e != null })
 | 
						|
				.reduce(r, n)
 | 
						|
				// XXX for some magical reason this breaks if...
 | 
						|
				// 		t = transformEditor($('.ribbon-set'))
 | 
						|
				// 		t.x()	// works ok
 | 
						|
				// 		var x = t.data
 | 
						|
				// 		t.x()	// now this breaks because reduce is 0...
 | 
						|
				 /*
 | 
						|
				.reduce(reduce == 'sum' ? function(a, b){ return a + b }
 | 
						|
						: reduce == 'mul' ? function(a, b){ return a * b }
 | 
						|
						: reduce == 'last' ? function(a, b){ return b != null ? b : a }
 | 
						|
						: reduce,
 | 
						|
					reduce == 'mul' ? 1
 | 
						|
						: reduce = 'sum'? 0
 | 
						|
						: 0)
 | 
						|
				 */
 | 
						|
		}
 | 
						|
 | 
						|
		// setup the aliases...
 | 
						|
		var aliases = Object.keys(spec)
 | 
						|
 | 
						|
		mode == 'last' && aliases.reverse()
 | 
						|
 | 
						|
		aliases.forEach(function(k){
 | 
						|
			var i = spec[k]
 | 
						|
 | 
						|
			var func = i instanceof Function ? i : handler
 | 
						|
 | 
						|
			// NOTE: we will pass the called alias name to the handler 
 | 
						|
			// 		via 'this'...
 | 
						|
			var f = editor[k]
 | 
						|
			var alias = editor[k] = f ? 
 | 
						|
				// wrap the original alias...
 | 
						|
				// NOTE: this will iterate the overloaded aliases... 
 | 
						|
				// 		i.e. this will iterate the arguments (width) while 
 | 
						|
				// 		the handler(..) will iterate the aliases...
 | 
						|
				function(){
 | 
						|
					var args = args2array(arguments)
 | 
						|
					// XXX do a full search through the alias values and merge results...
 | 
						|
					if(args.length == 0 && k in this.__direct){
 | 
						|
						return this.__direct[k].call(this)
 | 
						|
					}
 | 
						|
 | 
						|
					var a = f.apply(this, args)
 | 
						|
					var b = func.call(this, k, args)
 | 
						|
 | 
						|
					if(k in this.__direct){
 | 
						|
						return this.__direct[k].call(this)
 | 
						|
					}
 | 
						|
					return b
 | 
						|
				} 
 | 
						|
				: function(){ 
 | 
						|
					var args = args2array(arguments)
 | 
						|
					return func.call(this, k, args) 
 | 
						|
				}
 | 
						|
 | 
						|
			alias.isAlias = true
 | 
						|
			alias.reduce = reduce
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	// XXX get these from grammar...
 | 
						|
	// The Grammar...
 | 
						|
	func('translate', ['px', 'px'], 0)
 | 
						|
	func('translate3d', ['px', 'px', 'px'], 0)
 | 
						|
	func('translateX', ['px'], 0)
 | 
						|
	func('translateY', ['px'], 0)
 | 
						|
	func('translateZ', ['px'], 0)
 | 
						|
	alias({ translate3d: 0, translate: 0, translateX: 0, x: 0 }, 'sum')
 | 
						|
	alias({ translate3d: 1, translate: 1, translateY: 0, y: 0, }, 'sum')
 | 
						|
	alias({ translate3d: 2, translateZ: 0, z: 0, }, 'sum') 
 | 
						|
 | 
						|
	func('scale', ['', ''], 1)
 | 
						|
	//func('scale3d', ['', '', ''])
 | 
						|
	func('scaleX', [''], 1)
 | 
						|
	func('scaleY', [''], 1)
 | 
						|
	//func('scaleZ')
 | 
						|
	alias({ scale: 0, /*scale3d: 0,*/ scaleX: 0, }, 'mul')
 | 
						|
	alias({ scale: 1, /*scale3d: 1,*/ scaleY: 0, }, 'mul')
 | 
						|
	//alias({ scale3d: 2, scaleZ: 0, }, 'mul')
 | 
						|
 | 
						|
	// special case: single arg scale: scale(n) -> scale(n, n)
 | 
						|
	editor._scale = editor.scale
 | 
						|
	editor.scale = function(){
 | 
						|
		if(arguments.length == 1 
 | 
						|
				&& arguments[0] != '' 
 | 
						|
				&& arguments[0] != '='){
 | 
						|
			return this._scale.call(this, arguments[0], arguments[0])
 | 
						|
		}
 | 
						|
		var res = this._scale.apply(this, arguments)
 | 
						|
		// is this correct here???
 | 
						|
		return res.length == 2 && res[0] == res[1] ? res[0] : res
 | 
						|
	}
 | 
						|
 | 
						|
	func('rotate', ['deg'], 0)
 | 
						|
	func('rotate3d', ['px', 'px', 'px', 'deg'], 0)
 | 
						|
	func('rotateX', ['deg'], 0)
 | 
						|
	func('rotateY', ['deg'], 0)
 | 
						|
	func('rotateZ', ['deg'], 0)
 | 
						|
 | 
						|
	func('matrix') // XXX ???
 | 
						|
	func('matrix3d', [
 | 
						|
		'', '', '', '',
 | 
						|
		'', '', '', '',
 | 
						|
		'', '', '', '',
 | 
						|
		'', '', '', '',
 | 
						|
	])
 | 
						|
 | 
						|
	func('skew', ['', ''], 0)
 | 
						|
	func('skewX', [''], 0)
 | 
						|
	func('skewY', [''], 0)
 | 
						|
	alias({skewX: 0, skew: 0}, 'sum')
 | 
						|
	alias({skewY: 0, skew: 1}, 'sum')
 | 
						|
 | 
						|
	func('perspective')
 | 
						|
 | 
						|
 | 
						|
	// non-transform functions...
 | 
						|
	func('origin', ['px', 'px', 'px'], 0)
 | 
						|
 | 
						|
 | 
						|
	// proxy the undefined in aliases functions...
 | 
						|
	Object.keys(editor.__direct).forEach(function(k){
 | 
						|
		if(!(k in editor)){
 | 
						|
			editor[k] = function(){ 
 | 
						|
				var args = args2array(arguments)
 | 
						|
				editor.__direct[k].apply(this, args.length > 0 ? [args]: [])
 | 
						|
				return editor.__direct[k].call(this)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	})
 | 
						|
 | 
						|
	return editor
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
var TransformEditor = 
 | 
						|
module.TransformEditor =
 | 
						|
object.makeConstructor('TransformEditor', transformEditor())
 | 
						|
 | 
						|
// XXX STUB: for testing only...
 | 
						|
window.transformEditor = TransformEditor
 | 
						|
 | 
						|
 | 
						|
// jQuery API for the TransformEditor...
 | 
						|
jQuery.fn.transform = function(){
 | 
						|
	var e = $(this)
 | 
						|
	var elem = e[0]
 | 
						|
 | 
						|
	var args = args2array(arguments)
 | 
						|
	// normalize...
 | 
						|
	args = args.length == 0 
 | 
						|
			|| typeof(args[0]) == typeof('str') ? args
 | 
						|
		: args[0].constructor === Array 
 | 
						|
			|| args.length == 1 ? args[0]
 | 
						|
		: args
 | 
						|
 | 
						|
	// load the current state...
 | 
						|
	var transform = TransformEditor(elem)
 | 
						|
 | 
						|
	// get state...
 | 
						|
	if(args.constructor === Array){
 | 
						|
		if(args.length == 0){
 | 
						|
			// XXX get all attrs...
 | 
						|
			// XXX
 | 
						|
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		// get requested attrs...
 | 
						|
		return transform.get(args)
 | 
						|
 | 
						|
	// set state...
 | 
						|
	} else {
 | 
						|
		var v
 | 
						|
 | 
						|
		// load user inputs...
 | 
						|
		for(var k in args){
 | 
						|
			if(!(k in transform)){
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			v = args[k]
 | 
						|
 | 
						|
			transform[k].apply(transform, v instanceof Array ? v : [v])
 | 
						|
		}
 | 
						|
 | 
						|
		transform.toElem(this)
 | 
						|
	}
 | 
						|
 | 
						|
	return e
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// shorthands...
 | 
						|
jQuery.fn.scale = function(value){
 | 
						|
	if(arguments.length > 0){
 | 
						|
		return $(this).transform({scale: args2array(arguments)})
 | 
						|
 | 
						|
	} else {
 | 
						|
		return $(this).transform('scale')
 | 
						|
	}
 | 
						|
}
 | 
						|
// get element scale... 
 | 
						|
jQuery.fn.rscale = function(){
 | 
						|
	var res = 1
 | 
						|
	$(this).parents().toArray().forEach(function(e){
 | 
						|
		res *= $(e).scale() || 1
 | 
						|
	})
 | 
						|
	return res
 | 
						|
}
 | 
						|
jQuery.fn.origin = function(a, b, c){
 | 
						|
	if(a != null && b != null){
 | 
						|
		return $(this).transform({origin: [a, b, c == null ? 0 : c]})
 | 
						|
 | 
						|
	} else if(a == '' || a instanceof Array){
 | 
						|
		return $(this).transform({origin: a})
 | 
						|
 | 
						|
	} else {
 | 
						|
		return $(this).transform('origin')
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**********************************************************************
 | 
						|
* vim:set ts=4 sw=4 :                                                */
 | 
						|
return module })
 |