j;j+=3)a(n[j+0]),a(n[j+1]),a(n[j+2])}}};!function(e){e.fn.toSelector=function(){var t=e(this).get(0),a=t.tagName.toLowerCase(),r=e(this).attr("id"),i=t.className.split(/\s+/),n=a;return"undefined"!=typeof r&&(n+="#"+r),"undefined"!=typeof i&&(n+="."+i.join(".")),n}}(jQuery),Three.prototype.css=n,Three.prototype.find=find,Three.prototype.fn=c,Three.prototype.lookAt=function(){return this.fn.three.call(this,"lookAt",arguments)}});
\ No newline at end of file
diff --git a/build/jquery.three.js b/build/jquery.three.js
index 5714871..4481811 100644
--- a/build/jquery.three.js
+++ b/build/jquery.three.js
@@ -1,7 +1,7 @@
/**
* @name jquery.three
* jQuery Three() - jQuery extension with 3D methods (using Three.js)
- * Version: 0.8.0 (Sat, 30 Nov 2013 10:20:34 GMT)
+ * Version: 0.9.8 (Sun, 11 Dec 2016 13:05:25 GMT)
*
* @author makesites
* Created by: Makis Tracend (@tracend)
@@ -25,12 +25,12 @@ window.requestAnimFrame = ( function( callback ) {
(function (root, factory) {
- "use strict";
+ //"use strict";
- var define = define || false;
+ //var define = define || false;
var jquery = root.$ || root.jQuery || root.ender;
- if (define && typeof define === 'function' && define.amd) {
+ if (typeof define === 'function' && define.amd){
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else {
@@ -42,17 +42,22 @@ window.requestAnimFrame = ( function( callback ) {
// Local variables
var css, _css;
+var files = {};
+var origin = location.origin || location.protocol + "//" + location.hostname + (location.port ? ":" + location.port: "");
// Create a fn container for internal methods
var fn = {
- self : function(){ return this; }
- };
+ self : function(){ return this; }
+};
+
var defaults = {
- watch : false,
+ alpha: true,
+ clock: true,
+ watch: false,
//deps : { "THREE" : "http://cdnjs.cloudflare.com/ajax/libs/three.js/r54/three.min.js" }
- deps : {
+ deps: {
"THREE" : "https://raw.github.com/mrdoob/three.js/master/build/three.min.js"
//"FresnelShader" : ""
},
@@ -68,12 +73,16 @@ var fn = {
this.objects = {};
this.scenes = {};
this.cameras = {};
+ this.terrains = {};
this.materials = {};
// defining types (extandable)
this.groups = {
- "camera" : "cameras", "scene" : "scenes", "mesh" : "objects", "plane" : "objects", "cube" : "objects", "sphere" : "objects", "cylinder" : "objects", "material" : "materials"
+ "camera" : "cameras", "scene" : "scenes", "terrain" : "terrains", "mesh" : "objects", "plane" : "objects", "cube" : "objects", "sphere" : "objects", "cylinder" : "objects", "material" : "materials"
};
- // #43 - calculating 'actual' framerate
+
+ // init clock
+ if( this.options.clock ) this.clock = new THREE.Clock();
+ // #43 - calculating 'actual' framerate (use clock?)
this.frame = {
current: 0,
rate: 0,
@@ -107,7 +116,7 @@ Three.prototype = {
this.properties = this.setProperties();
// init renderer
- this.renderer = new THREE.WebGLRenderer();
+ this.renderer = new THREE.WebGLRenderer({ alpha: this.options.alpha });
this.renderer.setSize( this.properties.width, this.properties.height);
// condition this!
this.renderer.autoClear = false;
@@ -194,7 +203,7 @@ Three.prototype = {
} else {
// new frame, new count
this.frame.rate = this.frame.current;
- this.frame.current = 1; //start fron 1 to include running frame ;)
+ this.frame.current = 1; //start from 1 to include running frame ;)
this.frame.date = now;
}
// loop on the next click
@@ -202,22 +211,31 @@ Three.prototype = {
},
render : function() {
- // apply transformations
- $(this.el).trigger({
- type: "update",
- target: this
- });
- //
-
+ // init render
if( this.active.scene && this.active.camera ){
- // render the skybox as a first pass
+ // resize skybox to the limits of the active camera (far attribute)
if( this.active.skybox ){
- if( this.active.skybox.camera ) this.active.skybox.camera.rotation.copy( this.active.camera.rotation );
- this.renderer.render( this.active.skybox.scene, this.active.skybox.camera );
+ //if( this.active.skybox.camera ) this.active.skybox.camera.rotation.copy( this.active.camera.rotation );
+ //this.renderer.render( this.active.skybox.scene, this.active.skybox.camera );
+ var horizon = this.active.camera.far;
+ var scale = this.active.skybox.scale;
+ if( scale.x !== horizon )
+ scale.x = scale.y = scale.z = horizon;
+ // always center around camera...
+ var position = this.active.camera.position;
+ this.active.skybox.position.set( position.x, position.y, position.z );
}
+
this.renderer.render( this.active.scene, this.active.camera );
}
+ // update internal animation queue
+ this.fn.animate.update();
+ // trigger update event regardlesss
+ $(this.el).trigger({
+ type: "update",
+ target: this
+ });
},
show : function( ) { },
@@ -252,10 +270,10 @@ Three.prototype = {
this.cameras[i].updateProjectionMatrix();
}
// better way of targeting skybox???
- if( this.active.skybox ){
- this.active.skybox.camera.aspect = this.properties.aspect;
- this.active.skybox.camera.updateProjectionMatrix();
- }
+ //if( this.active.skybox ){
+ // this.active.skybox.camera.aspect = this.properties.aspect;
+ // this.active.skybox.camera.updateProjectionMatrix();
+ //}
this.renderer.setSize( this.properties.width, this.properties.height );
},
@@ -347,6 +365,8 @@ Three.prototype.getAttributes = function( html ){
var val = attr[i].value;
// check if it's a number...
data[key] = ( parseInt(val, 10) || val === "0" ) ? parseInt(val, 10) : val;
+ // convert boolean
+ if( data[key] === "false" || data[key] === "true" ) data[key] = JSON.parse( data[key] );
} else if( attr[i].name && attr[i].name.search("class") === 0 ){
// add classes
var classes = attr[i].value.split(" ");
@@ -390,17 +410,25 @@ css = function ( styles ){
return this;
};
-
// Internal functions
fn.css = {
styles: function (a){
var sheets = document.styleSheets, o = {};
+
// loop through stylesheets
- for(var i in sheets) {
- var rules = sheets[i].rules || sheets[i].cssRules;
+ for( var i in sheets ){
+ var sheet = sheets[i];
+ var isOutsideOfDomain = sheet.href && sheet.href.indexOf("/") !== 0 && sheet.href.indexOf(origin) !== 0;
+ // ignore sheets out-of-domain or without CSS rules
+ if(isOutsideOfDomain || sheet.cssRules === null) continue;
+ //
+ var rules = sheet.cssRules || sheet.rules;
for(var r in rules) {
// #21 - excluding :hover styles from parsing
if( rules[r].selectorText && rules[r].selectorText.search(":hover") > -1) continue;
+ // excluding other pseudo elements
+ if( rules[r].selectorText && rules[r].selectorText.search("::before") > -1) continue;
+ if( rules[r].selectorText && rules[r].selectorText.search("::after") > -1) continue;
try{
if(a.is(rules[r].selectorText)) {
o = $.extend(o, css2json(rules[r].style));
@@ -422,6 +450,13 @@ fn.css = {
for( var attr in css ){
// remove prefixes
var key = attr.replace('-webkit-','').replace('-moz-','');
+ // save attribute reference in the object
+ object._style = object._style || {};
+ object._shaders = object._shaders || {};
+ var changed = ( object._style[key] && object._style[key] == css[attr] ) ? false : true; // save old value?
+ object._style[key] = css[attr];
+ // parse only changed atrributes
+ if( !changed ) continue;
// supported attributes
switch(key){
// - width
@@ -453,7 +488,7 @@ fn.css = {
//this.fn.css.skybox.call(this, css[attr]);
this.webglLight({color : color});
} else {
- object.material.color.setHex(color);
+ setColor( object, color );
}
break;
// - transforms
@@ -489,25 +524,38 @@ fn.css = {
break;
// - animation
case "animation-duration":
- console.log( key, css[attr]);
+ this.fn.css.animation.duration = parseInt( css[attr], 10) * 1000; // convert seconds to milliseconds
break;
case "animation-timing":
- console.log( key, css[attr]);
+ this.fn.css.animation.easing = css[attr];
break;
case "animation-delay":
- console.log( key, css[attr]);
+ this.fn.css.animation.delay = css[attr];
break;
case "animation-iteration-count":
- console.log( key, css[attr]);
+ this.fn.css.animation.repeat = css[attr];
break;
case "animation-direction":
- console.log( key, css[attr]);
+ this.fn.css.animation.direction = css[attr];
break;
case "animation-fill-mode":
- console.log( key, css[attr]);
+ this.fn.css.animation.fill = css[attr];
break;
case "animation-name":
- console.log( key, css[attr]);
+ //console.log( key, css[attr]);
+ // assumption: name is the last animation attribute processed
+ this.fn.css.animation.name = css[attr];
+ this.animate( this.fn.css.animation, object );
+ this.fn.css.animation = {};
+ break;
+ case "animation-timing-function":
+ // duplicate of animation-timing?
+ // if counting steps, save the number
+ var steps = css[attr].match(/steps\((\d)/); // not closed..
+ this.fn.css.animation.easing = ( steps ) ? parseInt(steps[1], 10) : css[attr];
+ break;
+ case "animation-play-state":
+ this.fn.css.animation.state = css[attr];
break;
case "display":
// set it as the active one...
@@ -517,6 +565,8 @@ fn.css = {
if( object instanceof THREE.Scene){
this.fn.css.skybox.call(this, css[attr]);
} else if( object.type == "terrain" ){
+ // make sure this get's processed on the next tick
+ //utils.delay( this.fn.css.terrain.bind(this), 100, css[attr] );
this.fn.css.terrain.call(this, css[attr]);
} else if ( object instanceof THREE.Mesh ) {
this.fn.css.texture.call(this, object, css[attr]);
@@ -529,19 +579,73 @@ fn.css = {
} catch( e ){
console.log(e);
}
+ } else if( object instanceof THREE.Sprite ){
+ var src = css[attr].replace(/\s|url\(|"|'|\)/g, "");
+ object.material.map = utils.textureLoader( src );
+ }
+ break;
+ case "background-size":
+ if( object instanceof THREE.Sprite ){
+ //
+ this.fn.css.sprite.call(this, object, css[attr]);
+ }
+ break;
+ // "background-position" arrives split in axis
+ case "background-position-x":
+ if( object instanceof THREE.Sprite ){
+ // update sprite
+ this.fn.css.sprite.call(this, object);
+ }
+ break;
+ case "background-position-y":
+ if( object instanceof THREE.Sprite ){
+ // update sprite
+ this.fn.css.sprite.call(this, object);
}
break;
+ case "filter":
+ // variables
+ // - find element
+ var el, $el;
+ if( object instanceof THREE.Mesh && object.parent instanceof THREE.Object3D ){
+ // parent is always an object...?
+ el = object.parent;
+ $el = object.parent.$el;
+ } else {
+ el = object;
+ $el = object.$el;
+ }
+ // get URLs
+ var urls = css[attr].replace(/url\(|"|'|\)/g, "").split('|'); // all shaders in one url?
+ // prerequisite
+ if( !urls.length ) break;
+ // get shaders
+ el._shaders = el._shaders || {};
+ for( var i in urls ){
+ var name = urls[i].substring(urls[i].lastIndexOf('/')+1);
+ el._shaders[name] = utils.getFile( urls[i] );
+ }
+ // add helper method
+ el.getShader = function( name ){
+ return this._shaders[name] || null;
+ };
+ // trigger event
+ $el.trigger('css-filter');
+ break;
}
}
},
+ // temporary container for parsed (animation) attributes...
+ animation: {},
+
rotate: function( attr ){
var rot = {};
var val;
- // only supporting rotate3d for now...
+ //
if( attr.search("rotate3d") > -1 ){
// replace all the bits we don't need
val = attr.match(/rotate3d\(([\s\S]*?)\)/gi);
@@ -554,6 +658,32 @@ fn.css = {
z: parseFloat( val[2], 10 ) * parseFloat( val[3], 10 ) * (Math.PI/180)
};
+ } else if( attr.search("rotateX") > -1 ){
+ // axis based rotation
+ val = attr.match(/rotateX\(([\s\S]*?)\)/gi);
+ val = val[0].replace(/rotateX\(|deg|\)| /gi, "").
+ rot = {
+ x: parseFloat( val, 10 ) * (Math.PI/180)
+ };
+ } else if( attr.search("rotateY") > -1 ){
+ val = attr.match(/rotateY\(([\s\S]*?)\)/gi);
+ val = val[0].replace(/rotateY\(|deg|\)| /gi, "");
+ rot = {
+ y: parseFloat( val, 10 ) * (Math.PI/180)
+ };
+ } else if( attr.search("rotateZ") > -1 ){
+ val = attr.match(/rotateZ\(([\s\S]*?)\)/gi);
+ val = val[0].replace(/rotateZ\(|deg|\)| /gi, "");
+ rot = {
+ z: parseFloat( val, 10 ) * (Math.PI/180)
+ };
+ } else if( attr.search("rotate") > -1 ){
+ val = attr.match(/rotate\(([\s\S]*?)\)/gi);
+ val = val[0].replace(/rotate\(|deg|\)| /gi, "");
+ // if no axis is set assume Z?
+ rot = {
+ z: parseFloat( val, 10 ) * (Math.PI/180)
+ };
}
return rot;
@@ -585,7 +715,7 @@ fn.css = {
scale: function( attr ){
var size = {};
- // only supporting rotate3d for now...
+ // only supporting scale3d for now...
if( attr.search("scale3d") > -1 ){
// replace all the bits we don't need
var val = attr.match(/scale3d\(([\s\S]*?)\)/gi);
@@ -605,71 +735,75 @@ fn.css = {
},
texture: function( el, attr ){
- var map = attr.replace(/\s|url\(|\)/g, "");
- var material = this.webglMaterial({ map : map });
+ var material;
+ var img = attr.replace(/\s|url\(|"|'|\)/g, "").split(',');
+ if( img.length > 1 ){
+ // shader material
+ // add available shaders
+ var uniforms = {};
+ var params = {};
+ var textureCube = new THREE.CubeTextureLoader().load( img );
+ //var textureCube = app.layout.views.get('back').$3d.active.skybox.material.uniforms.tCube.value );
+ textureCube.format = THREE.RGBFormat;
+
+ uniforms.tCube = {
+ type: "t",
+ value: textureCube
+ };
+ // add shaders if available
+ if(el._shaders){
+ for( var i in el._shaders ){
+ var shader = el._shaders[i];
+ if( i.indexOf('.fs') > -1 ){
+ params.fragmentShader = shader;
+ }
+ if( i.indexOf('.vs') > -1 ){
+ params.vertexShader = shader;
+ }
+ }
+ }
+
+ params.uniforms = uniforms;
+ params.needsUpdate = true;
+
+ material = new THREE.ShaderMaterial( params );
+
+ } else {
+ // basic map material
+ material = this.webglMaterial({ map : img[0] });
+ }
el.material = material;
},
terrain: function( attr ){
- var object = this.last;
-
- var img = attr.replace(/\s|url\(|\)/g, "").split(',');
+ var terrain = this.last;
+ //var img = attr.replace(/\s|url\(|"|'|\)/g, "").split(',');
+ var img = attr.match(/url\(\s*[\'"]?(([^\\\\\'" \(\)]*(\\\\.)?)+)[\'"]?\s*\)/img);
+ //
if(img instanceof Array){
+ var heightmap, diffuse, specular;
for( var i in img ){
+ // clean url(...) content
+ img[i] = img[i].replace(/\s|url\(|"|'|\)/g, "");
- if( img[i].search("heightmap") > -1 ){
-
- var heightmapTexture = THREE.ImageUtils.loadTexture( img[i] );
- //var heightmapTexture = this.webglTexture( img[i] );
- object.material.uniforms.tDisplacement.value = heightmapTexture;
- object.material.uniforms.uDisplacementScale.value = 375;
- // heightmap also the second diffuse map?
- var diffuseTexture2 = heightmapTexture;
- diffuseTexture2.wrapS = diffuseTexture2.wrapT = THREE.RepeatWrapping;
-
- object.material.uniforms.tDiffuse2.value = diffuseTexture2;
- object.material.uniforms.enableDiffuse2.value = true;
-
- }
- if( img[i].search("diffuse") > -1 ){
-
- var diffuseTexture1 = THREE.ImageUtils.loadTexture( img[i] );
- //var diffuseTexture1 = this.webglTexture( img[i] );
- diffuseTexture1.wrapS = diffuseTexture1.wrapT = THREE.RepeatWrapping;
-
- object.material.uniforms.tDiffuse1.value = diffuseTexture1;
- object.material.uniforms.enableDiffuse1.value = true;
+ if( img[i].search("heightmap") > -1 ) heightmap = terrain.updateTexture('heightmap', img[i]);
- }
- if( img[i].search("specular") > -1 ){
-
- var specularMap = THREE.ImageUtils.loadTexture( img[i] );
- //var specularMap = this.webglTexture( img[i] );
- specularMap.wrapS = specularMap.wrapT = THREE.RepeatWrapping;
+ if( img[i].search("diffuse") > -1 ) diffuse = terrain.updateTexture('diffuse', img[i]);
- object.material.uniforms.tSpecular.value = specularMap;
- object.material.uniforms.enableSpecular.value = true;
+ if( img[i].search("specular") > -1 ) specular = terrain.updateTexture('specular', img[i]);
- }
}
+ // fallbacks
+ if(!heightmap && img[0]) terrain.updateTexture('heightmap', img[0]);
+ if(!diffuse && img[1]) terrain.updateTexture('diffuse', img[1]);
+ if(!specular && img[2]) terrain.updateTexture('specular', img[2]);
+
} else {
- // one image... which texture is it?...
+ // one image... assume it's both heightmap and texture
+ terrain.updateTexture('heightmap', img);
+ terrain.updateTexture('diffuse', img);
}
- /*
-
- leftovers ( normal and detail textures)
-
- //detailTexture.wrapS = detailTexture.wrapT = THREE.RepeatWrapping;
-
- //uniformsTerrain[ "tNormal" ].value = heightmapTexture;
- //uniformsTerrain[ "uNormalScale" ].value = 1;
-
- //uniformsTerrain[ "tDetail" ].value = detailTexture;
-
- //uniformsTerrain[ "uShininess" ].value = 30;
-
- */
},
@@ -677,7 +811,7 @@ fn.css = {
// remove any whitespace, the url(..) and
// attempt to break it into an array
- var img = attr.replace(/\s|url\(|\)/g, "").split(',');
+ var img = attr.replace(/\s|url\(|"|'|\)/g, "").split(',');
if(img instanceof Array){
// expect a six-pack of images
this.addSkybox( img );
@@ -686,6 +820,39 @@ fn.css = {
// this is one image... not implemented yet
}
+ },
+
+ sprite: function( el, attr ){
+ // wait for the image to load
+ var loaded = setInterval(function(){
+ // assume map is available...
+ if( !el.material.map.image || !el.material.map.image.width ) return;
+ // fallbacks
+ attr = attr || el._style["background-size"] || "0 0";
+ var size = attr.split(" ");
+ var width = parseFloat(size[0], 10);
+ var height = parseFloat(size[1], 10);
+ // get position from style
+ var x = parseFloat( el._style["background-position-x"] || 0 );
+ var y = parseFloat( el._style["background-position-y"] || 0 );
+
+ if( !width || !height ) return;
+
+ // image dimensions
+ var imgWidth = el.material.map.image.width;
+ var imgHeight = el.material.map.image.height;
+ //
+ //el.material.uvOffset.set(1 / 5, 0);
+ //el.material.uvScale.set(1 / 5, 1);
+ el.material.map.offset.set( (imgWidth - width - x) / imgWidth, (imgHeight - height - y) / imgHeight ); // start from top left...
+ el.material.map.repeat.set(width / imgWidth, height / imgHeight);
+ //el.scale.set( width, height, 1 );
+ //console.log("sprite loaded");
+ // stop loop
+ clearInterval(loaded);
+ }, 200);
+
+
}
};
@@ -728,19 +895,308 @@ var css2json = function (css){
return s;
};
+function setColor( object, color ){
+ object = object || {};
+ // prerequisite
+ if( !object.material ) return; // create material instead?
+ // in case we have more than one materials
+ if( object.material.materials ){
+ // check it it's an array first?
+ for(var i in object.material.materials ){
+ object.material.materials[i].color.setHex(color);
+ }
+ } else {
+ object.material.color.setHex(color);
+ }
+}
+
+Three.prototype.animate = function( options, el ){
+ //this.mesh.rotation.z = Date.now() / 1000;
-Three.prototype.animate = function(){
+ // fallbacks
+ options = options || {};
+ el = el || this.last || false; // last processed object
+ // FIX: we are checking the type of the element to attach to Object3D?
+ if( el instanceof THREE.Mesh && el.parent instanceof THREE.Object3D ){
+ el = el.parent;
+ }
+ // prerequisites
+ if( !el || !options.name ) return;
+ // create the necessary object containers
+ // should we be checking the type of the element to attach to Object3D?
+ el._animations = el._animations || {};
+ el._update = el._update || updateAnimations.bind( el );
+
+ // pickup animation keyframes
+ options.keyframes = this.fn.animate.getKeyframes.call( this, options.name );
+ // exit now...
+ if( !options.keyframes ) return;
+ // set the new animation
+ el._animations[ options.name ] = options;
+ // add to animate queue (once...)
+ var inQueue = ( typeof this.fn.animate.queue[el.uuid] !== "undefined" );
+ if( !inQueue){
+ this.fn.animate.queue[el.uuid] = el._update; // using uuid to be able to remove from queue later
+ }
+};
+
+// Internal
+
+fn.animate = {
+ // container for all methods to be updated
+ queue: {},
+
+ getKeyframes: function( name ){
+ var keyframes = {};
+ // first find the rules
+ var animation = findKeyframesRule( name );
+
+ if(!animation) return;
+ // parse each one of them
+ for(var i in animation.cssRules){
+ var rule = animation.cssRules[i];
+ var frame = {};
+ // FIX: only rules parsed
+ if( !rule.keyText ) continue;
+ // convert percent to 1-100 number
+ var key = parseInt( rule.keyText, 10 ), val;
+ // find rotation values
+ frame.rotation = this.fn.css.rotate( rule.cssText );
+ // find translate values
+ frame.translation = this.fn.css.translate( rule.cssText );
+ // find scale values
+ frame.scale = this.fn.css.scale( rule.cssText );
+ // other attributes
+ if( rule.cssText.search("background-position-x") > -1 ){
+ frame["background-position"] = frame["background-position"] || {};
+ val = rule.cssText.match(/:[\d|\s|\w]+\;/); // capture everything between : ;
+ if( val ) frame["background-position"].x = parseFloat(val[0].substr(1), 10);
+ }
+ if( rule.cssText.search("background-position-y") > -1 ){
+ frame["background-position"] = frame["background-position"] || {};
+ val = rule.cssText.match(/:[\d|\s|\w]+\;/); // capture everything between : ;
+ if( val ) frame["background-position"].y = parseFloat(val[0].substr(1), 10);
+ }
+ // add to the keyframes
+ keyframes[ key ] = frame;
+ }
+ return keyframes;
+ },
+
+ // loop through the object's animations and update the object's properties
+ update: function( el ){
+ //console.log( this.queue );
+ // loop through the queue
+ for( var i in this.queue ){
+ // execute
+ this.queue[i]();
+ }
+ }
+
+};
+
+// Helpers
+
+function updateAnimations(){
+ // context is the individual object
+ //console.log( this );
+ // loop through animations
+ for( var i in this._animations){
+ var animation = this._animations[i];
+ var keyframes = animation.keyframes;
+ // get current params
+ animation.start = animation.start || utils.now();
+ animation.end = animation.end || ( animation.start + animation.duration );
+ animation.offset = animation.offset || registerState( this );
+ animation.count = animation.count || 0;
+ // find the right stage in the animation
+ var now = utils.now();
+ var percent = ( ( now - animation.start) / animation.duration ) * 100;
+ var start = false,
+ end = false;
+ for( var key in keyframes ){
+ if( key <= percent ){
+ start = keyframes[ key ];
+ }
+ if( key > percent && !end ){
+ end = keyframes[ key ];
+ }
+ }
+ // fallbacks
+ if( !start ) start = keyframes[ 0 ];
+ if( !end ) end = keyframes[ 100 ];
+ // apply updates
+ // NOTE: only linear supported for now...
+ // - rotate
+ var rot = {
+ x: ( typeof start.rotation.x != "undefined" && typeof end.rotation.x != "undefined" ) ? (end.rotation.x - start.rotation.x )*(percent/100) : 0,
+ y: ( typeof start.rotation.y != "undefined" && typeof end.rotation.y != "undefined" ) ? (end.rotation.y - start.rotation.y )*(percent/100) : 0,
+ z: ( typeof start.rotation.z != "undefined" && typeof end.rotation.z != "undefined" ) ? (end.rotation.z - start.rotation.z )*(percent/100) : 0
+ };
+ this.rotation.set( animation.offset.rotation.x+rot.x, animation.offset.rotation.y+rot.y, animation.offset.rotation.z+ rot.z);
+ // TBA...
+ // - translate
+
+ // - scale
+
+ // - sprites
+ if( this instanceof THREE.Sprite ){
+ // get current sprite index
+ var offset = {
+ x: this.material.map.offset.x,
+ y: this.material.map.offset.y
+ };
+ // increment
+ var steps = animation.easing;
+ var step = 100/steps;
+ var nextStep = ( (parseInt( percent, 10) % step) === 0 ) ? parseInt( percent, 10) / 100 : 0; // every time we return to zero we have a new step
+ if( nextStep ){
+ // FIX: start index of steps from zero
+ nextStep -= 1/steps;
+ // find the right axis
+ if( typeof animation.keyframes[0]["background-position"].x !== "undefined" ){
+ // - numbers go in reverse order (steps-1 to 0)
+ offset.x = ((steps-1)/steps)-nextStep;
+ }
+ if( typeof animation.keyframes[0]["background-position"].y !== "undefined" ){
+ // - numbers go in reverse order (steps-1 to 0)
+ offset.y = ((steps-1)/steps)-nextStep;
+ }
+ // update sprite
+ this.material.map.offset.set( offset.x, offset.y );
+ }
+
+ }
+ // reset if reached completion
+ if( percent >= 100 ){
+ delete animation.start;
+ delete animation.end;
+ delete animation.offset;
+ animation.count++;
+ }
+ //
+ if( animation.count == animation.repeat ){
+ delete this._animations[i];
+ } else {
+ // save animation updates
+ this._animations[i] = animation;
+ }
+ }
+}
+
+// register element state
+function registerState( el ){
+ return {
+ position: {
+ x: el.position.x,
+ y: el.position.y,
+ z: el.position.z
+ },
+ rotation: {
+ x: el.rotation.x,
+ y: el.rotation.y,
+ z: el.rotation.z
+ },
+ scale: {
+ x: el.scale.x,
+ y: el.scale.y,
+ z: el.scale.z
+ }
+ };
+}
+
+/*
+ * Access and modify CSS animations @keyFrames with Javascript
+ * Based on : http://jsfiddle.net/russelluresti/RHhBz/2/
+ * Issue : http://stackoverflow.com/questions/10342494/set-webkit-keyframes-values-using-javascript-variable
+ */
+
+ // search the CSSOM for a specific -webkit-keyframe rule
+function findKeyframesRule(rule){
+ // gather all stylesheets into an array
+ var ss = document.styleSheets;
+
+ // loop through the stylesheets
+ for (var i = 0; i < ss.length; ++i) {
+
+ // loop through all the rules
+ for (var j = 0; j < ss[i].cssRules.length; ++j) {
+
+ // find the -webkit-keyframe rule whose name matches our passed over parameter and return that rule
+ if (ss[i].cssRules[j].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && ss[i].cssRules[j].name == rule)
+ return ss[i].cssRules[j];
+ }
+ }
+
+ // rule not found
+ return null;
+}
+
+// remove old keyframes and add new ones
+function changeAnimationKeyframes(anim){
+ // find our -webkit-keyframe rule
+ var keyframes = findKeyframesRule(anim);
+
+ // remove the existing 0% and 100% rules
+ keyframes.deleteRule("0%");
+ keyframes.deleteRule("100%");
+
+ // create new 0% and 100% rules with random numbers
+ keyframes.insertRule("0% { -webkit-transform: rotate("+randomFromTo(-360,360)+"deg); }");
+ keyframes.insertRule("100% { -webkit-transform: rotate("+randomFromTo(-360,360)+"deg); }");
+
+ // assign the animation to our element (which will cause the animation to run)
+ document.getElementById('box').style.webkitAnimationName = anim;
+}
+
+Three.prototype.fx = function(){
//this.mesh.rotation.z = Date.now() / 1000;
-
+
};
// watch an element for changes
Three.prototype.watch = function( el ) {
var self = this;
- var element = $(this.el).toSelector() +" "+ $( el ).selector;
+ var selector = $( el ).selector || "shadow-root"; // fallback to main root
+ var element = $(this.el).toSelector() +" "+ selector;
+
+ var node = document.getElementById("main").querySelector("shadow-root"), //$(this.el)[0], //$(element)[0],
+ bubbles = false;
+
+ // shim
+ var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
+
+ var whatToObserve = { childList: true, attributes: true, subtree: true, attributeOldValue: true, attributeFilter: ['class', 'style']};
+ //var whatToObserve = { childList: true, attributes: false, subtree: true };
+ var attrObserver = new MutationObserver(function(mutationRecords) {
+ $.each(mutationRecords, function(index, mutationRecord) {
+ var e = mutationRecord;
+ if (mutationRecord.type === 'childList') {
+ if (mutationRecord.addedNodes.length > 0) {
+ //DOM node added, do something
+ //console.log("execute", mutationRecord );
+ //console.log('DOMSubtreeModified', e.target.innerHTML.length, e.target );
+ self.eventSubtree(e);
+ } else if (mutationRecord.removedNodes.length > 0) {
+ //DOM node removed, do something
+ }
+ }
+ else if (mutationRecord.type === 'attributes') {
+ if (mutationRecord.attributeName === 'class') {
+ //class changed, do something
+ self.eventAttribute(e);
+ }
+ }
+ });
+ });
+
+ attrObserver.observe(node, whatToObserve);
+
// monitor new elements
+ /*
$('body').on('DOMSubtreeModified', element, function(e){
+ console.log(e);
self.eventSubtree(e);
});
// monitor attribute changes
@@ -754,13 +1210,39 @@ Three.prototype.watch = function( el ) {
self.eventAttribute(e);
});
}
+ */
+
+ var observeStyles = new MutationObserver(function (mutations) {
+ mutations.forEach(stylesModified);
+ });
+ observeStyles.observe(node, { childList: false, attributes: true, subtree: true, attributeFilter: ['class', 'style'] });
+
+
// monitor css style changes
+ function stylesModified(mutation) {
+ var el = mutation.target,
+ name = mutation.attributeName,
+ newValue = mutation.target.getAttribute(name),
+ oldValue = mutation.oldValue;
+ // skip all id, data-id updates (not editable from the user)
+ //if( name == 'id' || name == 'data-id' ) return;
+ // styling updates
+ if( name == 'style' ){
+ var object = self.objects[ el.getAttribute('data-id') ] || self.active.terrain;
+ var webgl = object.children[0] || object;
+ var css = css2json(newValue); // validate?
+ // HACK: why is terrain using this.last?
+ self.last = webgl;
+ self.fn.css.set.call(self, webgl, css );
+ }
+ //console.log(name, newValue, oldValue);
+ }
};
// - new element
Three.prototype.eventSubtree = function(e) {
- e.stopPropagation();
+ //e.stopPropagation(); // mutation event doesn't propagate?
// variables
var $root = $( $(this.el).toSelector() +" shadow-root" ).get(0);
@@ -770,21 +1252,29 @@ Three.prototype.eventSubtree = function(e) {
this.parent = ( $root == $target ) ? $(e.target) : $(e.target).parent();
this.target = $(e.target);
- if (e.target.innerHTML.length > 0) {
- // Handle new content
- //var html = e.target.innerHTML;
- var html = $(e.target).html();
- //this.newEl = $(e.target).children().last();
- // #46 parsing one tag at a time
- //html = $(html).html("").get(0);
- //this.newEl = $(html).last();
- this.append( html, { silent : true, target: this.target, traverse: false, watch: true });
- }
+ // exclude shadow-root
+ //if( $root == $target ) return;
+ // exclude empty targets
+ if(e.target.innerHTML.length === 0) return;
+ // Handle new content
+ //var html = e.target.innerHTML;
+ var html = $(e.target).html();
+ //var wrapper = $(html).eq(0)[0];
+ // FIX: exclude empty div tags (dead-ends)
+ //if( wrapper.tagName == "DIV" &&
+ //if( wrapper.toString().substr(0, 5).toLowerCase() == "