diff --git a/.gitignore b/.gitignore index bfc1f9e69..46c0ed5d3 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,6 @@ spine-love/spine-lua/ spine-love/love/ !spine-love/spine-lua/Place spine-lua here.txt -spine-starling +spine-as3/bin +spine-starling/spine-starling/bin +spine-starling/spine-starling-example/bin-debug diff --git a/spine-as3/.actionScriptProperties b/spine-as3/.actionScriptProperties new file mode 100644 index 000000000..ac85b98e6 --- /dev/null +++ b/spine-as3/.actionScriptProperties @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spine-as3/.flexLibProperties b/spine-as3/.flexLibProperties new file mode 100644 index 000000000..1fbacb45b --- /dev/null +++ b/spine-as3/.flexLibProperties @@ -0,0 +1,6 @@ + + + + + + diff --git a/spine-as3/.project b/spine-as3/.project new file mode 100644 index 000000000..6eb849cd5 --- /dev/null +++ b/spine-as3/.project @@ -0,0 +1,18 @@ + + + spine-as3 + + + + + + com.adobe.flexbuilder.project.flexbuilder + + + + + + com.adobe.flexbuilder.project.aslibnature + com.adobe.flexbuilder.project.actionscriptnature + + diff --git a/spine-as3/.settings/org.eclipse.core.resources.prefs b/spine-as3/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..7e697cc40 --- /dev/null +++ b/spine-as3/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Tue Apr 30 18:55:56 CEST 2013 +eclipse.preferences.version=1 +encoding/=utf-8 diff --git a/spine-as3/html-template/history/history.css b/spine-as3/html-template/history/history.css new file mode 100644 index 000000000..dbc47c61b --- /dev/null +++ b/spine-as3/html-template/history/history.css @@ -0,0 +1,6 @@ +/* This CSS stylesheet defines styles used by required elements in a flex application page that supports browser history */ + +#ie_historyFrame { width: 0px; height: 0px; display:none } +#firefox_anchorDiv { width: 0px; height: 0px; display:none } +#safari_formDiv { width: 0px; height: 0px; display:none } +#safari_rememberDiv { width: 0px; height: 0px; display:none } diff --git a/spine-as3/html-template/history/history.js b/spine-as3/html-template/history/history.js new file mode 100644 index 000000000..00a8bfee7 --- /dev/null +++ b/spine-as3/html-template/history/history.js @@ -0,0 +1,678 @@ +BrowserHistoryUtils = { + addEvent: function(elm, evType, fn, useCapture) { + useCapture = useCapture || false; + if (elm.addEventListener) { + elm.addEventListener(evType, fn, useCapture); + return true; + } + else if (elm.attachEvent) { + var r = elm.attachEvent('on' + evType, fn); + return r; + } + else { + elm['on' + evType] = fn; + } + } +} + +BrowserHistory = (function() { + // type of browser + var browser = { + ie: false, + ie8: false, + firefox: false, + safari: false, + opera: false, + version: -1 + }; + + // Default app state URL to use when no fragment ID present + var defaultHash = ''; + + // Last-known app state URL + var currentHref = document.location.href; + + // Initial URL (used only by IE) + var initialHref = document.location.href; + + // Initial URL (used only by IE) + var initialHash = document.location.hash; + + // History frame source URL prefix (used only by IE) + var historyFrameSourcePrefix = 'history/historyFrame.html?'; + + // History maintenance (used only by Safari) + var currentHistoryLength = -1; + + // Flag to denote the existence of onhashchange + var browserHasHashChange = false; + + var historyHash = []; + + var initialState = createState(initialHref, initialHref + '#' + initialHash, initialHash); + + var backStack = []; + var forwardStack = []; + + var currentObjectId = null; + + //UserAgent detection + var useragent = navigator.userAgent.toLowerCase(); + + if (useragent.indexOf("opera") != -1) { + browser.opera = true; + } else if (useragent.indexOf("msie") != -1) { + browser.ie = true; + browser.version = parseFloat(useragent.substring(useragent.indexOf('msie') + 4)); + if (browser.version == 8) + { + browser.ie = false; + browser.ie8 = true; + } + } else if (useragent.indexOf("safari") != -1) { + browser.safari = true; + browser.version = parseFloat(useragent.substring(useragent.indexOf('safari') + 7)); + } else if (useragent.indexOf("gecko") != -1) { + browser.firefox = true; + } + + if (browser.ie == true && browser.version == 7) { + window["_ie_firstload"] = false; + } + + function hashChangeHandler() + { + currentHref = document.location.href; + var flexAppUrl = getHash(); + //ADR: to fix multiple + if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { + var pl = getPlayers(); + for (var i = 0; i < pl.length; i++) { + pl[i].browserURLChange(flexAppUrl); + } + } else { + getPlayer().browserURLChange(flexAppUrl); + } + } + + // Accessor functions for obtaining specific elements of the page. + function getHistoryFrame() + { + return document.getElementById('ie_historyFrame'); + } + + function getFormElement() + { + return document.getElementById('safari_formDiv'); + } + + function getRememberElement() + { + return document.getElementById("safari_remember_field"); + } + + // Get the Flash player object for performing ExternalInterface callbacks. + // Updated for changes to SWFObject2. + function getPlayer(id) { + var i; + + if (id && document.getElementById(id)) { + var r = document.getElementById(id); + if (typeof r.SetVariable != "undefined") { + return r; + } + else { + var o = r.getElementsByTagName("object"); + var e = r.getElementsByTagName("embed"); + for (i = 0; i < o.length; i++) { + if (typeof o[i].browserURLChange != "undefined") + return o[i]; + } + for (i = 0; i < e.length; i++) { + if (typeof e[i].browserURLChange != "undefined") + return e[i]; + } + } + } + else { + var o = document.getElementsByTagName("object"); + var e = document.getElementsByTagName("embed"); + for (i = 0; i < e.length; i++) { + if (typeof e[i].browserURLChange != "undefined") + { + return e[i]; + } + } + for (i = 0; i < o.length; i++) { + if (typeof o[i].browserURLChange != "undefined") + { + return o[i]; + } + } + } + return undefined; + } + + function getPlayers() { + var i; + var players = []; + if (players.length == 0) { + var tmp = document.getElementsByTagName('object'); + for (i = 0; i < tmp.length; i++) + { + if (typeof tmp[i].browserURLChange != "undefined") + players.push(tmp[i]); + } + } + if (players.length == 0 || players[0].object == null) { + var tmp = document.getElementsByTagName('embed'); + for (i = 0; i < tmp.length; i++) + { + if (typeof tmp[i].browserURLChange != "undefined") + players.push(tmp[i]); + } + } + return players; + } + + function getIframeHash() { + var doc = getHistoryFrame().contentWindow.document; + var hash = String(doc.location.search); + if (hash.length == 1 && hash.charAt(0) == "?") { + hash = ""; + } + else if (hash.length >= 2 && hash.charAt(0) == "?") { + hash = hash.substring(1); + } + return hash; + } + + /* Get the current location hash excluding the '#' symbol. */ + function getHash() { + // It would be nice if we could use document.location.hash here, + // but it's faulty sometimes. + var idx = document.location.href.indexOf('#'); + return (idx >= 0) ? document.location.href.substr(idx+1) : ''; + } + + /* Get the current location hash excluding the '#' symbol. */ + function setHash(hash) { + // It would be nice if we could use document.location.hash here, + // but it's faulty sometimes. + if (hash == '') hash = '#' + document.location.hash = hash; + } + + function createState(baseUrl, newUrl, flexAppUrl) { + return { 'baseUrl': baseUrl, 'newUrl': newUrl, 'flexAppUrl': flexAppUrl, 'title': null }; + } + + /* Add a history entry to the browser. + * baseUrl: the portion of the location prior to the '#' + * newUrl: the entire new URL, including '#' and following fragment + * flexAppUrl: the portion of the location following the '#' only + */ + function addHistoryEntry(baseUrl, newUrl, flexAppUrl) { + + //delete all the history entries + forwardStack = []; + + if (browser.ie) { + //Check to see if we are being asked to do a navigate for the first + //history entry, and if so ignore, because it's coming from the creation + //of the history iframe + if (flexAppUrl == defaultHash && document.location.href == initialHref && window['_ie_firstload']) { + currentHref = initialHref; + return; + } + if ((!flexAppUrl || flexAppUrl == defaultHash) && window['_ie_firstload']) { + newUrl = baseUrl + '#' + defaultHash; + flexAppUrl = defaultHash; + } else { + // for IE, tell the history frame to go somewhere without a '#' + // in order to get this entry into the browser history. + getHistoryFrame().src = historyFrameSourcePrefix + flexAppUrl; + } + setHash(flexAppUrl); + } else { + + //ADR + if (backStack.length == 0 && initialState.flexAppUrl == flexAppUrl) { + initialState = createState(baseUrl, newUrl, flexAppUrl); + } else if(backStack.length > 0 && backStack[backStack.length - 1].flexAppUrl == flexAppUrl) { + backStack[backStack.length - 1] = createState(baseUrl, newUrl, flexAppUrl); + } + + if (browser.safari && !browserHasHashChange) { + // for Safari, submit a form whose action points to the desired URL + if (browser.version <= 419.3) { + var file = window.location.pathname.toString(); + file = file.substring(file.lastIndexOf("/")+1); + getFormElement().innerHTML = '
'; + //get the current elements and add them to the form + var qs = window.location.search.substring(1); + var qs_arr = qs.split("&"); + for (var i = 0; i < qs_arr.length; i++) { + var tmp = qs_arr[i].split("="); + var elem = document.createElement("input"); + elem.type = "hidden"; + elem.name = tmp[0]; + elem.value = tmp[1]; + document.forms.historyForm.appendChild(elem); + } + document.forms.historyForm.submit(); + } else { + top.location.hash = flexAppUrl; + } + // We also have to maintain the history by hand for Safari + historyHash[history.length] = flexAppUrl; + _storeStates(); + } else { + // Otherwise, just tell the browser to go there + setHash(flexAppUrl); + } + } + backStack.push(createState(baseUrl, newUrl, flexAppUrl)); + } + + function _storeStates() { + if (browser.safari) { + getRememberElement().value = historyHash.join(","); + } + } + + function handleBackButton() { + //The "current" page is always at the top of the history stack. + var current = backStack.pop(); + if (!current) { return; } + var last = backStack[backStack.length - 1]; + if (!last && backStack.length == 0){ + last = initialState; + } + forwardStack.push(current); + } + + function handleForwardButton() { + //summary: private method. Do not call this directly. + + var last = forwardStack.pop(); + if (!last) { return; } + backStack.push(last); + } + + function handleArbitraryUrl() { + //delete all the history entries + forwardStack = []; + } + + /* Called periodically to poll to see if we need to detect navigation that has occurred */ + function checkForUrlChange() { + + if (browser.ie) { + if (currentHref != document.location.href && currentHref + '#' != document.location.href) { + //This occurs when the user has navigated to a specific URL + //within the app, and didn't use browser back/forward + //IE seems to have a bug where it stops updating the URL it + //shows the end-user at this point, but programatically it + //appears to be correct. Do a full app reload to get around + //this issue. + if (browser.version < 7) { + currentHref = document.location.href; + document.location.reload(); + } else { + if (getHash() != getIframeHash()) { + // this.iframe.src = this.blankURL + hash; + var sourceToSet = historyFrameSourcePrefix + getHash(); + getHistoryFrame().src = sourceToSet; + currentHref = document.location.href; + } + } + } + } + + if (browser.safari && !browserHasHashChange) { + // For Safari, we have to check to see if history.length changed. + if (currentHistoryLength >= 0 && history.length != currentHistoryLength) { + //alert("did change: " + history.length + ", " + historyHash.length + "|" + historyHash[history.length] + "|>" + historyHash.join("|")); + var flexAppUrl = getHash(); + if (browser.version < 528.16 /* Anything earlier than Safari 4.0 */) + { + // If it did change and we're running Safari 3.x or earlier, + // then we have to look the old state up in our hand-maintained + // array since document.location.hash won't have changed, + // then call back into BrowserManager. + currentHistoryLength = history.length; + flexAppUrl = historyHash[currentHistoryLength]; + } + + //ADR: to fix multiple + if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { + var pl = getPlayers(); + for (var i = 0; i < pl.length; i++) { + pl[i].browserURLChange(flexAppUrl); + } + } else { + getPlayer().browserURLChange(flexAppUrl); + } + _storeStates(); + } + } + if (browser.firefox && !browserHasHashChange) { + if (currentHref != document.location.href) { + var bsl = backStack.length; + + var urlActions = { + back: false, + forward: false, + set: false + } + + if ((window.location.hash == initialHash || window.location.href == initialHref) && (bsl == 1)) { + urlActions.back = true; + // FIXME: could this ever be a forward button? + // we can't clear it because we still need to check for forwards. Ugg. + // clearInterval(this.locationTimer); + handleBackButton(); + } + + // first check to see if we could have gone forward. We always halt on + // a no-hash item. + if (forwardStack.length > 0) { + if (forwardStack[forwardStack.length-1].flexAppUrl == getHash()) { + urlActions.forward = true; + handleForwardButton(); + } + } + + // ok, that didn't work, try someplace back in the history stack + if ((bsl >= 2) && (backStack[bsl - 2])) { + if (backStack[bsl - 2].flexAppUrl == getHash()) { + urlActions.back = true; + handleBackButton(); + } + } + + if (!urlActions.back && !urlActions.forward) { + var foundInStacks = { + back: -1, + forward: -1 + } + + for (var i = 0; i < backStack.length; i++) { + if (backStack[i].flexAppUrl == getHash() && i != (bsl - 2)) { + arbitraryUrl = true; + foundInStacks.back = i; + } + } + for (var i = 0; i < forwardStack.length; i++) { + if (forwardStack[i].flexAppUrl == getHash() && i != (bsl - 2)) { + arbitraryUrl = true; + foundInStacks.forward = i; + } + } + handleArbitraryUrl(); + } + + // Firefox changed; do a callback into BrowserManager to tell it. + currentHref = document.location.href; + var flexAppUrl = getHash(); + //ADR: to fix multiple + if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { + var pl = getPlayers(); + for (var i = 0; i < pl.length; i++) { + pl[i].browserURLChange(flexAppUrl); + } + } else { + getPlayer().browserURLChange(flexAppUrl); + } + } + } + } + + var _initialize = function () { + + browserHasHashChange = ("onhashchange" in document.body); + + if (browser.ie) + { + var scripts = document.getElementsByTagName('script'); + for (var i = 0, s; s = scripts[i]; i++) { + if (s.src.indexOf("history.js") > -1) { + var iframe_location = (new String(s.src)).replace("history.js", "historyFrame.html"); + } + } + historyFrameSourcePrefix = iframe_location + "?"; + var src = historyFrameSourcePrefix; + + var iframe = document.createElement("iframe"); + iframe.id = 'ie_historyFrame'; + iframe.name = 'ie_historyFrame'; + iframe.src = 'javascript:false;'; + + try { + document.body.appendChild(iframe); + } catch(e) { + setTimeout(function() { + document.body.appendChild(iframe); + }, 0); + } + } + + if (browser.safari && !browserHasHashChange) + { + var rememberDiv = document.createElement("div"); + rememberDiv.id = 'safari_rememberDiv'; + document.body.appendChild(rememberDiv); + rememberDiv.innerHTML = ''; + + var formDiv = document.createElement("div"); + formDiv.id = 'safari_formDiv'; + document.body.appendChild(formDiv); + + var reloader_content = document.createElement('div'); + reloader_content.id = 'safarireloader'; + var scripts = document.getElementsByTagName('script'); + for (var i = 0, s; s = scripts[i]; i++) { + if (s.src.indexOf("history.js") > -1) { + html = (new String(s.src)).replace(".js", ".html"); + } + } + reloader_content.innerHTML = ''; + document.body.appendChild(reloader_content); + reloader_content.style.position = 'absolute'; + reloader_content.style.left = reloader_content.style.top = '-9999px'; + iframe = reloader_content.getElementsByTagName('iframe')[0]; + + if (document.getElementById("safari_remember_field").value != "" ) { + historyHash = document.getElementById("safari_remember_field").value.split(","); + } + } + + if (browserHasHashChange) + document.body.onhashchange = hashChangeHandler; + } + + return { + historyHash: historyHash, + backStack: function() { return backStack; }, + forwardStack: function() { return forwardStack }, + getPlayer: getPlayer, + initialize: function(src) { + _initialize(src); + }, + setURL: function(url) { + document.location.href = url; + }, + getURL: function() { + return document.location.href; + }, + getTitle: function() { + return document.title; + }, + setTitle: function(title) { + try { + backStack[backStack.length - 1].title = title; + } catch(e) { } + //if on safari, set the title to be the empty string. + if (browser.safari) { + if (title == "") { + try { + var tmp = window.location.href.toString(); + title = tmp.substring((tmp.lastIndexOf("/")+1), tmp.lastIndexOf("#")); + } catch(e) { + title = ""; + } + } + } + document.title = title; + }, + setDefaultURL: function(def) + { + defaultHash = def; + def = getHash(); + //trailing ? is important else an extra frame gets added to the history + //when navigating back to the first page. Alternatively could check + //in history frame navigation to compare # and ?. + if (browser.ie) + { + window['_ie_firstload'] = true; + var sourceToSet = historyFrameSourcePrefix + def; + var func = function() { + getHistoryFrame().src = sourceToSet; + window.location.replace("#" + def); + setInterval(checkForUrlChange, 50); + } + try { + func(); + } catch(e) { + window.setTimeout(function() { func(); }, 0); + } + } + + if (browser.safari) + { + currentHistoryLength = history.length; + if (historyHash.length == 0) { + historyHash[currentHistoryLength] = def; + var newloc = "#" + def; + window.location.replace(newloc); + } else { + //alert(historyHash[historyHash.length-1]); + } + setInterval(checkForUrlChange, 50); + } + + + if (browser.firefox || browser.opera) + { + var reg = new RegExp("#" + def + "$"); + if (window.location.toString().match(reg)) { + } else { + var newloc ="#" + def; + window.location.replace(newloc); + } + setInterval(checkForUrlChange, 50); + } + + }, + + /* Set the current browser URL; called from inside BrowserManager to propagate + * the application state out to the container. + */ + setBrowserURL: function(flexAppUrl, objectId) { + if (browser.ie && typeof objectId != "undefined") { + currentObjectId = objectId; + } + //fromIframe = fromIframe || false; + //fromFlex = fromFlex || false; + //alert("setBrowserURL: " + flexAppUrl); + //flexAppUrl = (flexAppUrl == "") ? defaultHash : flexAppUrl ; + + var pos = document.location.href.indexOf('#'); + var baseUrl = pos != -1 ? document.location.href.substr(0, pos) : document.location.href; + var newUrl = baseUrl + '#' + flexAppUrl; + + if (document.location.href != newUrl && document.location.href + '#' != newUrl) { + currentHref = newUrl; + addHistoryEntry(baseUrl, newUrl, flexAppUrl); + currentHistoryLength = history.length; + } + }, + + browserURLChange: function(flexAppUrl) { + var objectId = null; + if (browser.ie && currentObjectId != null) { + objectId = currentObjectId; + } + + if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { + var pl = getPlayers(); + for (var i = 0; i < pl.length; i++) { + try { + pl[i].browserURLChange(flexAppUrl); + } catch(e) { } + } + } else { + try { + getPlayer(objectId).browserURLChange(flexAppUrl); + } catch(e) { } + } + + currentObjectId = null; + }, + getUserAgent: function() { + return navigator.userAgent; + }, + getPlatform: function() { + return navigator.platform; + } + + } + +})(); + +// Initialization + +// Automated unit testing and other diagnostics + +function setURL(url) +{ + document.location.href = url; +} + +function backButton() +{ + history.back(); +} + +function forwardButton() +{ + history.forward(); +} + +function goForwardOrBackInHistory(step) +{ + history.go(step); +} + +//BrowserHistoryUtils.addEvent(window, "load", function() { BrowserHistory.initialize(); }); +(function(i) { + var u =navigator.userAgent;var e=/*@cc_on!@*/false; + var st = setTimeout; + if(/webkit/i.test(u)){ + st(function(){ + var dr=document.readyState; + if(dr=="loaded"||dr=="complete"){i()} + else{st(arguments.callee,10);}},10); + } else if((/mozilla/i.test(u)&&!/(compati)/.test(u)) || (/opera/i.test(u))){ + document.addEventListener("DOMContentLoaded",i,false); + } else if(e){ + (function(){ + var t=document.createElement('doc:rdy'); + try{t.doScroll('left'); + i();t=null; + }catch(e){st(arguments.callee,0);}})(); + } else{ + window.onload=i; + } +})( function() {BrowserHistory.initialize();} ); diff --git a/spine-as3/html-template/history/historyFrame.html b/spine-as3/html-template/history/historyFrame.html new file mode 100644 index 000000000..07e3806f0 --- /dev/null +++ b/spine-as3/html-template/history/historyFrame.html @@ -0,0 +1,29 @@ + + + + + + + + Hidden frame for Browser History support. + + diff --git a/spine-as3/html-template/index.template.html b/spine-as3/html-template/index.template.html new file mode 100644 index 000000000..d327daded --- /dev/null +++ b/spine-as3/html-template/index.template.html @@ -0,0 +1,108 @@ + + + + + + ${title} + + + + + + + + + + + + + +
+

+ To view this page ensure that Adobe Flash Player version + ${version_major}.${version_minor}.${version_revision} or greater is installed. +

+ +
+ + + + diff --git a/spine-as3/html-template/playerProductInstall.swf b/spine-as3/html-template/playerProductInstall.swf new file mode 100644 index 000000000..bdc343785 Binary files /dev/null and b/spine-as3/html-template/playerProductInstall.swf differ diff --git a/spine-as3/html-template/swfobject.js b/spine-as3/html-template/swfobject.js new file mode 100644 index 000000000..bf35c07c8 --- /dev/null +++ b/spine-as3/html-template/swfobject.js @@ -0,0 +1,777 @@ +/*! SWFObject v2.2 + is released under the MIT License +*/ + +var swfobject = function() { + + var UNDEF = "undefined", + OBJECT = "object", + SHOCKWAVE_FLASH = "Shockwave Flash", + SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash", + FLASH_MIME_TYPE = "application/x-shockwave-flash", + EXPRESS_INSTALL_ID = "SWFObjectExprInst", + ON_READY_STATE_CHANGE = "onreadystatechange", + + win = window, + doc = document, + nav = navigator, + + plugin = false, + domLoadFnArr = [main], + regObjArr = [], + objIdArr = [], + listenersArr = [], + storedAltContent, + storedAltContentId, + storedCallbackFn, + storedCallbackObj, + isDomLoaded = false, + isExpressInstallActive = false, + dynamicStylesheet, + dynamicStylesheetMedia, + autoHideShow = true, + + /* Centralized function for browser feature detection + - User agent string detection is only used when no good alternative is possible + - Is executed directly for optimal performance + */ + ua = function() { + var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF, + u = nav.userAgent.toLowerCase(), + p = nav.platform.toLowerCase(), + windows = p ? /win/.test(p) : /win/.test(u), + mac = p ? /mac/.test(p) : /mac/.test(u), + webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit + ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html + playerVersion = [0,0,0], + d = null; + if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) { + d = nav.plugins[SHOCKWAVE_FLASH].description; + if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+ + plugin = true; + ie = false; // cascaded feature detection for Internet Explorer + d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1"); + playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10); + playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10); + playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0; + } + } + else if (typeof win.ActiveXObject != UNDEF) { + try { + var a = new ActiveXObject(SHOCKWAVE_FLASH_AX); + if (a) { // a will return null when ActiveX is disabled + d = a.GetVariable("$version"); + if (d) { + ie = true; // cascaded feature detection for Internet Explorer + d = d.split(" ")[1].split(","); + playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; + } + } + } + catch(e) {} + } + return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac }; + }(), + + /* Cross-browser onDomLoad + - Will fire an event as soon as the DOM of a web page is loaded + - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/ + - Regular onload serves as fallback + */ + onDomLoad = function() { + if (!ua.w3) { return; } + if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically + callDomLoadFunctions(); + } + if (!isDomLoaded) { + if (typeof doc.addEventListener != UNDEF) { + doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false); + } + if (ua.ie && ua.win) { + doc.attachEvent(ON_READY_STATE_CHANGE, function() { + if (doc.readyState == "complete") { + doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee); + callDomLoadFunctions(); + } + }); + if (win == top) { // if not inside an iframe + (function(){ + if (isDomLoaded) { return; } + try { + doc.documentElement.doScroll("left"); + } + catch(e) { + setTimeout(arguments.callee, 0); + return; + } + callDomLoadFunctions(); + })(); + } + } + if (ua.wk) { + (function(){ + if (isDomLoaded) { return; } + if (!/loaded|complete/.test(doc.readyState)) { + setTimeout(arguments.callee, 0); + return; + } + callDomLoadFunctions(); + })(); + } + addLoadEvent(callDomLoadFunctions); + } + }(); + + function callDomLoadFunctions() { + if (isDomLoaded) { return; } + try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early + var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span")); + t.parentNode.removeChild(t); + } + catch (e) { return; } + isDomLoaded = true; + var dl = domLoadFnArr.length; + for (var i = 0; i < dl; i++) { + domLoadFnArr[i](); + } + } + + function addDomLoadEvent(fn) { + if (isDomLoaded) { + fn(); + } + else { + domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+ + } + } + + /* Cross-browser onload + - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/ + - Will fire an event as soon as a web page including all of its assets are loaded + */ + function addLoadEvent(fn) { + if (typeof win.addEventListener != UNDEF) { + win.addEventListener("load", fn, false); + } + else if (typeof doc.addEventListener != UNDEF) { + doc.addEventListener("load", fn, false); + } + else if (typeof win.attachEvent != UNDEF) { + addListener(win, "onload", fn); + } + else if (typeof win.onload == "function") { + var fnOld = win.onload; + win.onload = function() { + fnOld(); + fn(); + }; + } + else { + win.onload = fn; + } + } + + /* Main function + - Will preferably execute onDomLoad, otherwise onload (as a fallback) + */ + function main() { + if (plugin) { + testPlayerVersion(); + } + else { + matchVersions(); + } + } + + /* Detect the Flash Player version for non-Internet Explorer browsers + - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description: + a. Both release and build numbers can be detected + b. Avoid wrong descriptions by corrupt installers provided by Adobe + c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports + - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available + */ + function testPlayerVersion() { + var b = doc.getElementsByTagName("body")[0]; + var o = createElement(OBJECT); + o.setAttribute("type", FLASH_MIME_TYPE); + var t = b.appendChild(o); + if (t) { + var counter = 0; + (function(){ + if (typeof t.GetVariable != UNDEF) { + var d = t.GetVariable("$version"); + if (d) { + d = d.split(" ")[1].split(","); + ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; + } + } + else if (counter < 10) { + counter++; + setTimeout(arguments.callee, 10); + return; + } + b.removeChild(o); + t = null; + matchVersions(); + })(); + } + else { + matchVersions(); + } + } + + /* Perform Flash Player and SWF version matching; static publishing only + */ + function matchVersions() { + var rl = regObjArr.length; + if (rl > 0) { + for (var i = 0; i < rl; i++) { // for each registered object element + var id = regObjArr[i].id; + var cb = regObjArr[i].callbackFn; + var cbObj = {success:false, id:id}; + if (ua.pv[0] > 0) { + var obj = getElementById(id); + if (obj) { + if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match! + setVisibility(id, true); + if (cb) { + cbObj.success = true; + cbObj.ref = getObjectById(id); + cb(cbObj); + } + } + else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported + var att = {}; + att.data = regObjArr[i].expressInstall; + att.width = obj.getAttribute("width") || "0"; + att.height = obj.getAttribute("height") || "0"; + if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); } + if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); } + // parse HTML object param element's name-value pairs + var par = {}; + var p = obj.getElementsByTagName("param"); + var pl = p.length; + for (var j = 0; j < pl; j++) { + if (p[j].getAttribute("name").toLowerCase() != "movie") { + par[p[j].getAttribute("name")] = p[j].getAttribute("value"); + } + } + showExpressInstall(att, par, id, cb); + } + else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF + displayAltContent(obj); + if (cb) { cb(cbObj); } + } + } + } + else { // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content) + setVisibility(id, true); + if (cb) { + var o = getObjectById(id); // test whether there is an HTML object element or not + if (o && typeof o.SetVariable != UNDEF) { + cbObj.success = true; + cbObj.ref = o; + } + cb(cbObj); + } + } + } + } + } + + function getObjectById(objectIdStr) { + var r = null; + var o = getElementById(objectIdStr); + if (o && o.nodeName == "OBJECT") { + if (typeof o.SetVariable != UNDEF) { + r = o; + } + else { + var n = o.getElementsByTagName(OBJECT)[0]; + if (n) { + r = n; + } + } + } + return r; + } + + /* Requirements for Adobe Express Install + - only one instance can be active at a time + - fp 6.0.65 or higher + - Win/Mac OS only + - no Webkit engines older than version 312 + */ + function canExpressInstall() { + return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312); + } + + /* Show the Adobe Express Install dialog + - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75 + */ + function showExpressInstall(att, par, replaceElemIdStr, callbackFn) { + isExpressInstallActive = true; + storedCallbackFn = callbackFn || null; + storedCallbackObj = {success:false, id:replaceElemIdStr}; + var obj = getElementById(replaceElemIdStr); + if (obj) { + if (obj.nodeName == "OBJECT") { // static publishing + storedAltContent = abstractAltContent(obj); + storedAltContentId = null; + } + else { // dynamic publishing + storedAltContent = obj; + storedAltContentId = replaceElemIdStr; + } + att.id = EXPRESS_INSTALL_ID; + if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; } + if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; } + doc.title = doc.title.slice(0, 47) + " - Flash Player Installation"; + var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn", + fv = "MMredirectURL=" + encodeURI(window.location).toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title; + if (typeof par.flashvars != UNDEF) { + par.flashvars += "&" + fv; + } + else { + par.flashvars = fv; + } + // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it, + // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work + if (ua.ie && ua.win && obj.readyState != 4) { + var newObj = createElement("div"); + replaceElemIdStr += "SWFObjectNew"; + newObj.setAttribute("id", replaceElemIdStr); + obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf + obj.style.display = "none"; + (function(){ + if (obj.readyState == 4) { + obj.parentNode.removeChild(obj); + } + else { + setTimeout(arguments.callee, 10); + } + })(); + } + createSWF(att, par, replaceElemIdStr); + } + } + + /* Functions to abstract and display alternative content + */ + function displayAltContent(obj) { + if (ua.ie && ua.win && obj.readyState != 4) { + // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it, + // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work + var el = createElement("div"); + obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content + el.parentNode.replaceChild(abstractAltContent(obj), el); + obj.style.display = "none"; + (function(){ + if (obj.readyState == 4) { + obj.parentNode.removeChild(obj); + } + else { + setTimeout(arguments.callee, 10); + } + })(); + } + else { + obj.parentNode.replaceChild(abstractAltContent(obj), obj); + } + } + + function abstractAltContent(obj) { + var ac = createElement("div"); + if (ua.win && ua.ie) { + ac.innerHTML = obj.innerHTML; + } + else { + var nestedObj = obj.getElementsByTagName(OBJECT)[0]; + if (nestedObj) { + var c = nestedObj.childNodes; + if (c) { + var cl = c.length; + for (var i = 0; i < cl; i++) { + if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) { + ac.appendChild(c[i].cloneNode(true)); + } + } + } + } + } + return ac; + } + + /* Cross-browser dynamic SWF creation + */ + function createSWF(attObj, parObj, id) { + var r, el = getElementById(id); + if (ua.wk && ua.wk < 312) { return r; } + if (el) { + if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content + attObj.id = id; + } + if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML + var att = ""; + for (var i in attObj) { + if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries + if (i.toLowerCase() == "data") { + parObj.movie = attObj[i]; + } + else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword + att += ' class="' + attObj[i] + '"'; + } + else if (i.toLowerCase() != "classid") { + att += ' ' + i + '="' + attObj[i] + '"'; + } + } + } + var par = ""; + for (var j in parObj) { + if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries + par += ''; + } + } + el.outerHTML = '' + par + ''; + objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only) + r = getElementById(attObj.id); + } + else { // well-behaving browsers + var o = createElement(OBJECT); + o.setAttribute("type", FLASH_MIME_TYPE); + for (var m in attObj) { + if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries + if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword + o.setAttribute("class", attObj[m]); + } + else if (m.toLowerCase() != "classid") { // filter out IE specific attribute + o.setAttribute(m, attObj[m]); + } + } + } + for (var n in parObj) { + if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element + createObjParam(o, n, parObj[n]); + } + } + el.parentNode.replaceChild(o, el); + r = o; + } + } + return r; + } + + function createObjParam(el, pName, pValue) { + var p = createElement("param"); + p.setAttribute("name", pName); + p.setAttribute("value", pValue); + el.appendChild(p); + } + + /* Cross-browser SWF removal + - Especially needed to safely and completely remove a SWF in Internet Explorer + */ + function removeSWF(id) { + var obj = getElementById(id); + if (obj && obj.nodeName == "OBJECT") { + if (ua.ie && ua.win) { + obj.style.display = "none"; + (function(){ + if (obj.readyState == 4) { + removeObjectInIE(id); + } + else { + setTimeout(arguments.callee, 10); + } + })(); + } + else { + obj.parentNode.removeChild(obj); + } + } + } + + function removeObjectInIE(id) { + var obj = getElementById(id); + if (obj) { + for (var i in obj) { + if (typeof obj[i] == "function") { + obj[i] = null; + } + } + obj.parentNode.removeChild(obj); + } + } + + /* Functions to optimize JavaScript compression + */ + function getElementById(id) { + var el = null; + try { + el = doc.getElementById(id); + } + catch (e) {} + return el; + } + + function createElement(el) { + return doc.createElement(el); + } + + /* Updated attachEvent function for Internet Explorer + - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks + */ + function addListener(target, eventType, fn) { + target.attachEvent(eventType, fn); + listenersArr[listenersArr.length] = [target, eventType, fn]; + } + + /* Flash Player and SWF content version matching + */ + function hasPlayerVersion(rv) { + var pv = ua.pv, v = rv.split("."); + v[0] = parseInt(v[0], 10); + v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0" + v[2] = parseInt(v[2], 10) || 0; + return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false; + } + + /* Cross-browser dynamic CSS creation + - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php + */ + function createCSS(sel, decl, media, newStyle) { + if (ua.ie && ua.mac) { return; } + var h = doc.getElementsByTagName("head")[0]; + if (!h) { return; } // to also support badly authored HTML pages that lack a head element + var m = (media && typeof media == "string") ? media : "screen"; + if (newStyle) { + dynamicStylesheet = null; + dynamicStylesheetMedia = null; + } + if (!dynamicStylesheet || dynamicStylesheetMedia != m) { + // create dynamic stylesheet + get a global reference to it + var s = createElement("style"); + s.setAttribute("type", "text/css"); + s.setAttribute("media", m); + dynamicStylesheet = h.appendChild(s); + if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) { + dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1]; + } + dynamicStylesheetMedia = m; + } + // add style rule + if (ua.ie && ua.win) { + if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) { + dynamicStylesheet.addRule(sel, decl); + } + } + else { + if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) { + dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}")); + } + } + } + + function setVisibility(id, isVisible) { + if (!autoHideShow) { return; } + var v = isVisible ? "visible" : "hidden"; + if (isDomLoaded && getElementById(id)) { + getElementById(id).style.visibility = v; + } + else { + createCSS("#" + id, "visibility:" + v); + } + } + + /* Filter to avoid XSS attacks + */ + function urlEncodeIfNecessary(s) { + var regex = /[\\\"<>\.;]/; + var hasBadChars = regex.exec(s) != null; + return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s; + } + + /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only) + */ + var cleanup = function() { + if (ua.ie && ua.win) { + window.attachEvent("onunload", function() { + // remove listeners to avoid memory leaks + var ll = listenersArr.length; + for (var i = 0; i < ll; i++) { + listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]); + } + // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect + var il = objIdArr.length; + for (var j = 0; j < il; j++) { + removeSWF(objIdArr[j]); + } + // cleanup library's main closures to avoid memory leaks + for (var k in ua) { + ua[k] = null; + } + ua = null; + for (var l in swfobject) { + swfobject[l] = null; + } + swfobject = null; + }); + } + }(); + + return { + /* Public API + - Reference: http://code.google.com/p/swfobject/wiki/documentation + */ + registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) { + if (ua.w3 && objectIdStr && swfVersionStr) { + var regObj = {}; + regObj.id = objectIdStr; + regObj.swfVersion = swfVersionStr; + regObj.expressInstall = xiSwfUrlStr; + regObj.callbackFn = callbackFn; + regObjArr[regObjArr.length] = regObj; + setVisibility(objectIdStr, false); + } + else if (callbackFn) { + callbackFn({success:false, id:objectIdStr}); + } + }, + + getObjectById: function(objectIdStr) { + if (ua.w3) { + return getObjectById(objectIdStr); + } + }, + + embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) { + var callbackObj = {success:false, id:replaceElemIdStr}; + if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) { + setVisibility(replaceElemIdStr, false); + addDomLoadEvent(function() { + widthStr += ""; // auto-convert to string + heightStr += ""; + var att = {}; + if (attObj && typeof attObj === OBJECT) { + for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs + att[i] = attObj[i]; + } + } + att.data = swfUrlStr; + att.width = widthStr; + att.height = heightStr; + var par = {}; + if (parObj && typeof parObj === OBJECT) { + for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs + par[j] = parObj[j]; + } + } + if (flashvarsObj && typeof flashvarsObj === OBJECT) { + for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs + if (typeof par.flashvars != UNDEF) { + par.flashvars += "&" + k + "=" + flashvarsObj[k]; + } + else { + par.flashvars = k + "=" + flashvarsObj[k]; + } + } + } + if (hasPlayerVersion(swfVersionStr)) { // create SWF + var obj = createSWF(att, par, replaceElemIdStr); + if (att.id == replaceElemIdStr) { + setVisibility(replaceElemIdStr, true); + } + callbackObj.success = true; + callbackObj.ref = obj; + } + else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install + att.data = xiSwfUrlStr; + showExpressInstall(att, par, replaceElemIdStr, callbackFn); + return; + } + else { // show alternative content + setVisibility(replaceElemIdStr, true); + } + if (callbackFn) { callbackFn(callbackObj); } + }); + } + else if (callbackFn) { callbackFn(callbackObj); } + }, + + switchOffAutoHideShow: function() { + autoHideShow = false; + }, + + ua: ua, + + getFlashPlayerVersion: function() { + return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] }; + }, + + hasFlashPlayerVersion: hasPlayerVersion, + + createSWF: function(attObj, parObj, replaceElemIdStr) { + if (ua.w3) { + return createSWF(attObj, parObj, replaceElemIdStr); + } + else { + return undefined; + } + }, + + showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) { + if (ua.w3 && canExpressInstall()) { + showExpressInstall(att, par, replaceElemIdStr, callbackFn); + } + }, + + removeSWF: function(objElemIdStr) { + if (ua.w3) { + removeSWF(objElemIdStr); + } + }, + + createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) { + if (ua.w3) { + createCSS(selStr, declStr, mediaStr, newStyleBoolean); + } + }, + + addDomLoadEvent: addDomLoadEvent, + + addLoadEvent: addLoadEvent, + + getQueryParamValue: function(param) { + var q = doc.location.search || doc.location.hash; + if (q) { + if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark + if (param == null) { + return urlEncodeIfNecessary(q); + } + var pairs = q.split("&"); + for (var i = 0; i < pairs.length; i++) { + if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) { + return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1))); + } + } + } + return ""; + }, + + // For internal usage only + expressInstallCallback: function() { + if (isExpressInstallActive) { + var obj = getElementById(EXPRESS_INSTALL_ID); + if (obj && storedAltContent) { + obj.parentNode.replaceChild(storedAltContent, obj); + if (storedAltContentId) { + setVisibility(storedAltContentId, true); + if (ua.ie && ua.win) { storedAltContent.style.display = "block"; } + } + if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); } + } + isExpressInstallActive = false; + } + } + }; +}(); diff --git a/spine-as3/src/spine/AnimationState.as b/spine-as3/src/spine/AnimationState.as new file mode 100644 index 000000000..409b5d6a7 --- /dev/null +++ b/spine-as3/src/spine/AnimationState.as @@ -0,0 +1,155 @@ +package spine { +import spine.animation.Animation; + +public class AnimationState { + private var _data:AnimationStateData; + private var current:Animation; + private var previous:Animation; + private var currentTime:Number; + private var previousTime:Number; + private var currentLoop:Boolean; + private var previousLoop:Boolean; + private var mixTime:Number; + private var mixDuration:Number; + private var queue:Vector. = new Vector.(); + + public function AnimationState (data:AnimationStateData) { + if (data == null) + throw new ArgumentError("data cannot be null."); + _data = data; + } + + public function update (delta:Number) : void { + currentTime += delta; + previousTime += delta; + mixTime += delta; + + if (queue.length > 0) { + var entry:QueueEntry = queue[0]; + if (currentTime >= entry.delay) { + setAnimationInternal(entry.animation, entry.loop); + queue.shift(); + } + } + } + + public function apply (skeleton:Skeleton) : void { + if (!current) + return; + if (previous) { + previous.apply(skeleton, previousTime, previousLoop); + var alpha:Number = mixTime / mixDuration; + if (alpha >= 1) { + alpha = 1; + previous = null; + } + current.mix(skeleton, currentTime, currentLoop, alpha); + } else + current.apply(skeleton, currentTime, currentLoop); + } + + public function clearAnimation () : void { + previous = null; + current = null; + clearQueue(); + } + + private function clearQueue () : void { + queue.length = 0; + } + + private function setAnimationInternal (animation:Animation, loop:Boolean) : void { + previous = null; + if (animation != null && current != null) { + mixDuration = _data.getMix(current, animation); + if (mixDuration > 0) { + mixTime = 0; + previous = current; + previousTime = currentTime; + previousLoop = currentLoop; + } + } + current = animation; + currentLoop = loop; + currentTime = 0; + } + + /** @see #setAnimation(Animation, Boolean) */ + public function setAnimationByName (animationName:String, loop:Boolean) : void { + var animation:Animation = _data.skeletonData.findAnimation(animationName); + if (animation == null) + throw new ArgumentError("Animation not found: " + animationName); + setAnimation(animation, loop); + } + + /** Set the current animation. Any queued animations are cleared and the current animation time is set to 0. + * @param animation May be null. */ + public function setAnimation (animation:Animation, loop:Boolean) : void { + clearQueue(); + setAnimationInternal(animation, loop); + } + + /** @see #addAnimation(Animation, Boolean, Number) */ + public function addAnimationByName (animationName:String, loop:Boolean, delay:Number) : void { + var animation:Animation = _data.skeletonData.findAnimation(animationName); + if (animation == null) + throw new ArgumentError("Animation not found: " + animationName); + addAnimation(animation, loop, delay); + } + + /** Adds an animation to be played delay seconds after the current or last queued animation. + * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ + public function addAnimation (animation:Animation, loop:Boolean, delay:Number) : void { + var entry:QueueEntry = new QueueEntry(); + entry.animation = animation; + entry.loop = loop; + + if (delay <= 0) { + var previousAnimation:Animation = queue.length == 0 ? current : queue[queue.length - 1].animation; + if (previousAnimation != null) + delay = previousAnimation.duration - _data.getMix(previousAnimation, animation) + delay; + else + delay = 0; + } + entry.delay = delay; + + queue.push(entry); + } + + /** @return May be null. */ + public function get animation () : Animation { + return current; + } + + /** Returns the time within the current animation. */ + public function get time () : Number { + return currentTime; + } + + public function set time (time:Number) : void { + currentTime = time; + } + + /** Returns true if no animation is set or if the current time is greater than the animation duration, regardless of looping. */ + public function get isComplete () : Boolean { + return current == null || currentTime >= current.duration; + } + + public function get data () : AnimationStateData { + return _data; + } + + public function toString () : String { + return (current != null && current.name != null) ? current.name : super.toString(); + } +} + +} + +import spine.animation.Animation; + +class QueueEntry { + public var animation:Animation; + public var loop:Boolean; + public var delay:Number; +} diff --git a/spine-as3/src/spine/AnimationStateData.as b/spine-as3/src/spine/AnimationStateData.as new file mode 100644 index 000000000..0f2261067 --- /dev/null +++ b/spine-as3/src/spine/AnimationStateData.as @@ -0,0 +1,42 @@ +package spine { +import spine.animation.Animation; + +public class AnimationStateData { + private var _skeletonData:SkeletonData; + private var animationToMixTime:Object = new Object(); + + public function AnimationStateData (skeletonData:SkeletonData) { + _skeletonData = skeletonData; + } + + public function get skeletonData () : SkeletonData { + return _skeletonData; + } + + public function setMixByName (fromName:String, toName:String, duration:Number) : void { + var from:Animation = _skeletonData.findAnimation(fromName); + if (from == null) + throw new ArgumentError("Animation not found: " + fromName); + var to:Animation = _skeletonData.findAnimation(toName); + if (to == null) + throw new ArgumentError("Animation not found: " + toName); + setMix(from, to, duration); + } + + public function setMix (from:Animation, to:Animation, duration:Number) : void { + if (from == null) + throw new ArgumentError("from cannot be null."); + if (to == null) + throw new ArgumentError("to cannot be null."); + animationToMixTime[from.name + ":" + to.name] = duration; + } + + public function getMix (from:Animation, to:Animation) : Number { + var time:Object = animationToMixTime[from.name + ":" + to.name]; + if (time == null) + return 0; + return time as Number; + } +} + +} diff --git a/spine-as3/src/spine/Bone.as b/spine-as3/src/spine/Bone.as new file mode 100644 index 000000000..a63321144 --- /dev/null +++ b/spine-as3/src/spine/Bone.as @@ -0,0 +1,126 @@ +package spine { + +public class Bone { + static public var yDown:Boolean; + + internal var _data:BoneData; + internal var _parent:Bone; + public var x:Number; + public var y:Number; + public var rotation:Number; + public var scaleX:Number + public var scaleY:Number; + + internal var _m00:Number; + internal var _m01:Number; + internal var _m10:Number; + internal var _m11:Number; + internal var _worldX:Number; + internal var _worldY:Number; + internal var _worldRotation:Number; + internal var _worldScaleX:Number; + internal var _worldScaleY:Number; + + /** @param parent May be null. */ + public function Bone (data:BoneData, parent:Bone) { + if (data == null) + throw new ArgumentError("data cannot be null."); + _data = data; + _parent = parent; + setToBindPose(); + } + + /** Computes the world SRT using the parent bone and the local SRT. */ + public function updateWorldTransform (flipX:Boolean, flipY:Boolean) : void { + if (_parent != null) { + _worldX = x * _parent._m00 + y * _parent._m01 + _parent._worldX; + _worldY = x * _parent._m10 + y * _parent._m11 + _parent._worldY; + _worldScaleX = _parent._worldScaleX * scaleX; + _worldScaleY = _parent._worldScaleY * scaleY; + _worldRotation = _parent._worldRotation + rotation; + } else { + _worldX = x; + _worldY = y; + _worldScaleX = scaleX; + _worldScaleY = scaleY; + _worldRotation = rotation; + } + var radians:Number = _worldRotation * Math.PI / 180; + var cos:Number = Math.cos(radians); + var sin:Number = Math.sin(radians); + _m00 = cos * _worldScaleX; + _m10 = sin * _worldScaleX; + _m01 = -sin * _worldScaleY; + _m11 = cos * _worldScaleY; + if (flipX) { + _m00 = -_m00; + _m01 = -_m01; + } + if (flipY) { + _m10 = -_m10; + _m11 = -_m11; + } + if (yDown) { + _m10 = -_m10; + _m11 = -_m11; + } + } + + public function setToBindPose () : void { + x = _data.x; + y = _data.y; + rotation = _data.rotation; + scaleX = _data.scaleX; + scaleY = _data.scaleY; + } + + public function get data () : BoneData { + return _data; + } + + public function get parent () : Bone { + return _parent; + } + + public function get m00 () : Number { + return _m00; + } + + public function get m01 () : Number { + return _m01; + } + + public function get m10 () : Number { + return _m10; + } + + public function get m11 () : Number { + return _m11; + } + + public function get worldX () : Number { + return _worldX; + } + + public function get worldY () : Number { + return _worldY; + } + + public function get worldRotation () : Number { + return _worldRotation; + } + + public function get worldScaleX () : Number { + return _worldScaleX; + } + + public function get worldScaleY () : Number { + return _worldScaleY; + } + + public function toString () : String { + return _data._name; + } +} + +} diff --git a/spine-as3/src/spine/BoneData.as b/spine-as3/src/spine/BoneData.as new file mode 100644 index 000000000..f73b2d298 --- /dev/null +++ b/spine-as3/src/spine/BoneData.as @@ -0,0 +1,35 @@ +package spine { + +public class BoneData { + internal var _parent:BoneData; + internal var _name:String; + public var length:Number; + public var x:Number; + public var y:Number; + public var rotation:Number; + public var scaleX:Number = 1; + public var scaleY:Number = 1; + + /** @param parent May be null. */ + public function BoneData (name:String, parent:BoneData) { + if (name == null) + throw new ArgumentError("name cannot be null."); + _name = name; + _parent = parent; + } + + /** @return May be null. */ + public function get parent () : BoneData { + return _parent; + } + + public function get name () : String { + return _name; + } + + public function toString () : String { + return _name; + } +} + +} diff --git a/spine-as3/src/spine/Skeleton.as b/spine-as3/src/spine/Skeleton.as new file mode 100644 index 000000000..80973ad1c --- /dev/null +++ b/spine-as3/src/spine/Skeleton.as @@ -0,0 +1,197 @@ +package spine { +import spine.attachments.Attachment; + +public class Skeleton { + internal var _data:SkeletonData; + internal var _bones:Vector.; + internal var _slots:Vector.; + internal var _drawOrder:Vector.; + internal var _skin:Skin; + public var r:int = 1; + public var g:int = 1; + public var b:int = 1; + public var a:int = 1; + public var time:Number; + public var flipX:Boolean; + public var flipY:Boolean; + + public function Skeleton (data:SkeletonData) { + if (data == null) + throw new ArgumentError("data cannot be null."); + _data = data; + + _bones = new Vector.(); + for each (var boneData:BoneData in data.bones) { + var parent:Bone = boneData.parent == null ? null : _bones[data.bones.indexOf(boneData.parent)]; + _bones.push(new Bone(boneData, parent)); + } + + _slots = new Vector.(); + _drawOrder = new Vector.(); + for each (var slotData:SlotData in data.slots) { + var bone:Bone = _bones[data.bones.indexOf(slotData.boneData)]; + var slot:Slot = new Slot(slotData, this, bone); + _slots.push(slot); + _drawOrder.push(slot); + } + } + + /** Updates the world transform for each bone. */ + public function updateWorldTransform () : void { + for each (var bone:Bone in _bones) + bone.updateWorldTransform(flipX, flipY); + } + + /** Sets the bones and slots to their bind pose values. */ + public function setToBindPose () : void { + setBonesToBindPose(); + setSlotsToBindPose(); + } + + public function setBonesToBindPose () : void { + for each (var bone:Bone in _bones) + bone.setToBindPose(); + } + + public function setSlotsToBindPose () : void { + for each (var slot:Slot in _slots) + slot.setToBindPose(); + } + + public function get data () : SkeletonData { + return _data; + } + + public function get bones () : Vector. { + return _bones; + } + + public function get rootBone () : SkeletonData { + if (_bones.length == 0) + return null; + return _bones[0]; + } + + /** @return May be null. */ + public function findBone (boneName:String) : Bone { + if (boneName == null) + throw new ArgumentError("boneName cannot be null."); + for each (var bone:Bone in _bones) + if (bone.data.name == boneName) + return bone; + return null; + } + + /** @return -1 if the bone was not found. */ + public function findBoneIndex (boneName:String) : int { + if (boneName == null) + throw new ArgumentError("boneName cannot be null."); + var i:int = 0; + for each (var bone:Bone in _bones) { + if (bone.data.name == boneName) + return i; + i++; + } + return -1; + } + + public function get slots () : Vector. { + return _slots; + } + + /** @return May be null. */ + public function findSlot (slotName:String) : Slot { + if (slotName == null) + throw new ArgumentError("slotName cannot be null."); + for each (var slot:Slot in _slots) + if (slot.data.name == slotName) + return slot; + return null; + } + + /** @return -1 if the bone was not found. */ + public function findSlotIndex (slotName:String) : int { + if (slotName == null) + throw new ArgumentError("slotName cannot be null."); + var i:int = 0; + for each (var slot:Slot in _slots) { + if (slot.data.name == slotName) + return i; + i++; + } + return -1; + } + + public function get drawOrder () : Vector. { + return _drawOrder; + } + + public function get skin () : Skin { + return _skin; + } + + public function set skinName (skinName:String) : void { + var skin:Skin = data.findSkin(skinName); + if (skin == null) + throw new ArgumentError("Skin not found: " + skinName); + this.skin = skin; + } + + /** Sets the skin used to look up attachments not found in the {@link SkeletonData#getDefaultSkin() default skin}. Attachments + * from the new skin are attached if the corresponding attachment from the old skin was attached. + * @param newSkin May be null. */ + public function set skin (newSkin:Skin) : void { + if (skin != null && newSkin != null) + newSkin.attachAll(this, skin); + skin = newSkin; + } + + /** @return May be null. */ + public function getAttachmentForSlotName (slotName:String, attachmentName:String) : Attachment { + return getAttachmentForSlotIndex(data.findSlotIndex(slotName), attachmentName); + } + + /** @return May be null. */ + public function getAttachmentForSlotIndex (slotIndex:int, attachmentName:String) : Attachment { + if (attachmentName == null) + throw new ArgumentError("attachmentName cannot be null."); + if (skin != null) { + var attachment:Attachment = skin.getAttachment(slotIndex, attachmentName); + if (attachment != null) + return attachment; + } + if (data.defaultSkin != null) + return data.defaultSkin.getAttachment(slotIndex, attachmentName); + return null; + } + + /** @param attachmentName May be null. */ + public function setAttachment (slotName:String, attachmentName:String) : void { + if (slotName == null) + throw new ArgumentError("slotName cannot be null."); + var i:int = 0; + for each (var slot:Slot in _slots) { + if (slot.data.name == slotName) { + var attachment:Attachment = null; + if (attachmentName != null) { + attachment = getAttachmentForSlotIndex(i, attachmentName); + if (attachment == null) + throw new ArgumentError("Attachment not found: " + attachmentName + ", for slot: " + slotName); + } + slot.attachment = attachment; + return; + } + } + throw new ArgumentError("Slot not found: " + slotName); + } + + public function update (delta:Number) : void { + time += delta; + } + + public function toString () : String { + return _data.name != null ? _data.name : super.toString(); + } +} + +} diff --git a/spine-as3/src/spine/SkeletonData.as b/spine-as3/src/spine/SkeletonData.as new file mode 100644 index 000000000..743348de4 --- /dev/null +++ b/spine-as3/src/spine/SkeletonData.as @@ -0,0 +1,117 @@ +package spine { +import spine.animation.Animation; + +public class SkeletonData { + public var name:String; + public var bones:Vector. = new Vector.(); // Ordered parents first. + public var slots:Vector. = new Vector.(); // Bind pose draw order. + public var skins:Vector. = new Vector.(); + public var defaultSkin:Skin; + public var animations:Vector. = new Vector.(); + + // --- Bones. + + public function addBone (bone:BoneData) : void { + if (bone == null) + throw new ArgumentError("bone cannot be null."); + bones.push(bone); + } + + /** @return May be null. */ + public function findBone (boneName:String) : BoneData { + if (boneName == null) + throw new ArgumentError("boneName cannot be null."); + for (var i:int = 0, n:int = bones.length; i < n; i++) { + var bone:BoneData = bones[i]; + if (bone._name == boneName) + return bone; + } + return null; + } + + /** @return -1 if the bone was not found. */ + public function findBoneIndex (boneName:String) : int { + if (boneName == null) + throw new ArgumentError("boneName cannot be null."); + for (var i:int = 0, n:int = bones.length; i < n; i++) + if (bones[i]._name == boneName) + return i; + return -1; + } + + // --- Slots. + + public function addSlot (slot:SlotData) : void { + if (slot == null) + throw new ArgumentError("slot cannot be null."); + slots.push(slot); + } + + /** @return May be null. */ + public function findSlot (slotName:String) : SlotData { + if (slotName == null) + throw new ArgumentError("slotName cannot be null."); + for (var i:int = 0, n:int = slots.length; i < n; i++) { + var slot:SlotData = slots[i]; + if (slot._name == slotName) + return slot; + } + return null; + } + + /** @return -1 if the bone was not found. */ + public function findSlotIndex (slotName:String) : int { + if (slotName == null) + throw new ArgumentError("slotName cannot be null."); + for (var i:int = 0, n:int = slots.length; i < n; i++) + if (slots[i]._name == slotName) + return i; + return -1; + } + + // --- Skins. + + public function addSkin (skin:Skin) : void { + if (skin == null) + throw new ArgumentError("skin cannot be null."); + skins.push(skin); + } + + /** @return May be null. */ + public function findSkin (skinName:String) : Skin { + if (skinName == null) + throw new ArgumentError("skinName cannot be null."); + for each (var skin:Skin in skins) + if (skin._name == skinName) + return skin; + return null; + } + + // --- Animations. + + public function addAnimation (animation:Animation) : void { + if (animation == null) + throw new ArgumentError("animation cannot be null."); + animations.push(animation); + } + + /** @return May be null. */ + public function findAnimation (animationName:String) : Animation { + if (animationName == null) + throw new ArgumentError("animationName cannot be null."); + for (var i:int = 0, n:int = animations.length; i < n; i++) { + var animation:Animation = animations[i]; + if (animation.name == animationName) + return animation; + } + return null; + } + + // --- + + public function toString () : String { + return name != null ? name : super.toString(); + } +} + +} diff --git a/spine-as3/src/spine/SkeletonJson.as b/spine-as3/src/spine/SkeletonJson.as new file mode 100644 index 000000000..483511b6e --- /dev/null +++ b/spine-as3/src/spine/SkeletonJson.as @@ -0,0 +1,249 @@ +package spine { +import spine.animation.Animation; +import spine.animation.AttachmentTimeline; +import spine.animation.ColorTimeline; +import spine.animation.CurveTimeline; +import spine.animation.RotateTimeline; +import spine.animation.ScaleTimeline; +import spine.animation.Timeline; +import spine.animation.TranslateTimeline; +import spine.attachments.Attachment; +import spine.attachments.AttachmentLoader; +import spine.attachments.AttachmentType; +import spine.attachments.RegionAttachment; + +public class SkeletonJson { + static public const TIMELINE_SCALE:String = "scale"; + static public const TIMELINE_ROTATE:String = "rotate"; + static public const TIMELINE_TRANSLATE:String = "translate"; + static public const TIMELINE_ATTACHMENT:String = "attachment"; + static public const TIMELINE_COLOR:String = "color"; + + public var attachmentLoader:AttachmentLoader; + public var scale:Number = 1; + + public function SkeletonJson (attachmentLoader:AttachmentLoader = null) { + this.attachmentLoader = attachmentLoader; + } + + public function readSkeletonData (json:String, name:String) : SkeletonData { + if (json == null) + throw new ArgumentError("json cannot be null."); + + var skeletonData:SkeletonData = new SkeletonData(); + skeletonData.name = name; + + var root:Object = JSON.parse(json); + + // Bones. + var boneData:BoneData; + for each (var boneMap:Object in root["bones"]) { + var parent:BoneData = null; + var parentName:String = boneMap["parent"]; + if (parentName) { + parent = skeletonData.findBone(parentName); + if (!parent) + throw new Error("Parent bone not found: " + parentName); + } + boneData = new BoneData(boneMap["name"], parent); + boneData.length = (boneMap["length"] || 0) * scale; + boneData.x = (boneMap["x"] || 0) * scale; + boneData.y = (boneMap["y"] || 0) * scale; + boneData.rotation = (boneMap["rotation"] || 0); + boneData.scaleX = boneMap["scaleX"] || 1; + boneData.scaleY = boneMap["scaleY"] || 1; + skeletonData.addBone(boneData); + } + + // Slots. + for each (var slotMap:Object in root["slots"]) { + var boneName:String = slotMap["bone"]; + boneData = skeletonData.findBone(boneName); + if (!boneData) + throw new Error("Slot bone not found: " + boneName); + var slotData:SlotData = new SlotData(slotMap["name"], boneData); + + var color:String = slotMap["color"]; + if (color) { + slotData.r = toColor(color, 0); + slotData.g = toColor(color, 1); + slotData.b = toColor(color, 2); + slotData.a = toColor(color, 3); + } + + slotData.attachmentName = slotMap["attachment"]; + + skeletonData.addSlot(slotData); + } + + // Skins. + var skins:Object = root["skins"]; + for (var skinName:String in skins) { + var skinMap:Object = skins[skinName]; + var skin:Skin = new Skin(skinName); + for (var slotName:String in skinMap) { + var slotIndex:int = skeletonData.findSlotIndex(slotName); + var slotEntry:Object = skinMap[slotName]; + for (var attachmentName:String in slotEntry) { + var attachment:Attachment = readAttachment(skin, attachmentName, slotEntry[attachmentName]); + if (attachment != null) + skin.addAttachment(slotIndex, attachmentName, attachment); + } + } + skeletonData.addSkin(skin); + if (skin.name == "default") + skeletonData.defaultSkin = skin; + } + + // Animations. + var animations:Object = root["animations"]; + for (var animationName:String in animations) + readAnimation(animationName, animations[animationName], skeletonData); + + return skeletonData; + } + + private function readAttachment (skin:Skin, name:String, map:Object) : Attachment { + name = map["name"] || name; + + var type:AttachmentType = AttachmentType.valueOf(map["type"] || "region"); + var attachment:Attachment = attachmentLoader.newAttachment(skin, type, name); + + if (attachment is RegionAttachment) { + var regionAttachment:RegionAttachment = attachment as RegionAttachment; + regionAttachment.x = (map["x"] || 0) * scale; + regionAttachment.y = (map["y"] || 0) * scale; + regionAttachment.scaleX = map["scaleX"] || 1; + regionAttachment.scaleY = map["scaleY"] || 1; + regionAttachment.rotation = map["rotation"] || 0; + regionAttachment.width = (map["width"] || 32) * scale; + regionAttachment.height = (map["height"] || 32) * scale; + regionAttachment.updateOffset(); + } + + return attachment; + } + + private function readAnimation (name:String, map:Object, skeletonData:SkeletonData) : void { + var timelines:Vector. = new Vector.(); + var duration:Number = 0; + + var bones:Object = map["bones"]; + for (var boneName:String in bones) { + var boneIndex:int = skeletonData.findBoneIndex(boneName); + if (boneIndex == -1) + throw new Error("Bone not found: " + boneName); + var boneMap:Object = bones[boneName]; + + for (var timelineName:Object in boneMap) { + var timelineMap:Object = boneMap[timelineName]; + if (timelineName == TIMELINE_ROTATE) { + var timeline:RotateTimeline = new RotateTimeline(count(timelineMap)); + timeline.boneIndex = boneIndex; + + var frameIndex:int = 0; + for each (var valueMap:Object in timelineMap) { + timeline.setFrame(frameIndex, valueMap["time"], valueMap["angle"]); + readCurve(timeline, frameIndex, valueMap); + frameIndex++; + } + timelines.push(timeline); + duration = Math.max(duration, timeline.frames[timeline.frameCount * 2 - 2]); + + } else if (timelineName == TIMELINE_TRANSLATE || timelineName == TIMELINE_SCALE) { + var timeline1:TranslateTimeline; + var timelineScale:Number = 1; + if (timelineName == TIMELINE_SCALE) + timeline1 = new ScaleTimeline(count(timelineMap)); + else { + timeline1 = new TranslateTimeline(count(timelineMap)); + timelineScale = scale; + } + timeline1.boneIndex = boneIndex; + + var frameIndex1:int = 0; + for each (var valueMap1:Object in timelineMap) { + var x:Number = (valueMap1["x"] || 0) * timelineScale; + var y:Number = (valueMap1["y"] || 0) * timelineScale; + timeline1.setFrame(frameIndex1, valueMap1["time"], x, y); + readCurve(timeline1, frameIndex1, valueMap1); + frameIndex1++; + } + timelines.push(timeline1); + duration = Math.max(duration, timeline1.frames[timeline1.frameCount * 3 - 3]); + + } else + throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")"); + } + } + + var slots:Object = map["slots"]; + for (var slotName:String in slots) { + var slotMap:Object = slots[slotName]; + var slotIndex:int = skeletonData.findSlotIndex(slotName); + + for (var timelineName2:Object in boneMap) { + var timelineMap2:Object = boneMap[timelineName2]; + if (timelineName2 == TIMELINE_COLOR) { + var timeline2:ColorTimeline = new ColorTimeline(count(timelineMap2)); + timeline2.slotIndex = slotIndex; + + var frameIndex2:int = 0; + for each (var valueMap2:Object in timelineMap2) { + var color:String = valueMap["color"]; + var r:Number = toColor(color, 0); + var g:Number = toColor(color, 1); + var b:Number = toColor(color, 2); + var a:Number = toColor(color, 3); + timeline2.setFrame(frameIndex2, valueMap2["time"], r, g, b, a); + readCurve(timeline2, frameIndex2, valueMap); + frameIndex2++; + } + timelines.push(timeline2); + duration = Math.max(duration, timeline2.frames[timeline2.frameCount * 5 - 5]); + + } else if (timelineName2 == TIMELINE_ATTACHMENT) { + var timeline3:AttachmentTimeline = new AttachmentTimeline(count(timelineMap2)); + timeline3.slotIndex = slotIndex; + + var frameIndex3:int = 0; + for each (var valueMap3:Object in timelineMap2) { + timeline3.setFrame(frameIndex3++, valueMap3["time"], valueMap3["name"]); + } + timelines.push(timeline); + duration = Math.max(duration, timeline3.frames[timeline3.frameCount - 1]); + + } else + throw new Error("Invalid timeline type for a slot: " + timelineName2 + " (" + slotName + ")"); + } + } + + skeletonData.addAnimation(new Animation(name, timelines, duration)); + } + + private function readCurve (timeline:CurveTimeline, frameIndex:int, valueMap:Object) : void { + var curve:Object = valueMap["curve"]; + if (curve == null) + return; + if (curve == "stepped") + timeline.setStepped(frameIndex); + else if (curve is Array) { + timeline.setCurve(frameIndex, curve[0], curve[1], curve[2], curve[3]); + } + } + + static private function toColor (hexString:String, colorIndex:int) : Number { + if (hexString.length != 8) + throw new ArgumentError("Color hexidecimal length must be 8, recieved: " + hexString); + return parseInt(hexString.substring(colorIndex * 2, 2), 16) / 255; + } + + static private function count (map:Object) : int { + var count:int = 0; + for (var key:String in map) + count++; + return count; + } +} + +} diff --git a/spine-as3/src/spine/Skin.as b/spine-as3/src/spine/Skin.as new file mode 100644 index 000000000..68d21d7e1 --- /dev/null +++ b/spine-as3/src/spine/Skin.as @@ -0,0 +1,50 @@ +package spine { +import spine.attachments.Attachment; + +/** Stores attachments by slot index and attachment name. */ +public class Skin { + internal var _name:String; + private var attachments:Object = new Object(); + + public function Skin (name:String) { + if (name == null) + throw new ArgumentError("name cannot be null."); + _name = name; + } + + public function addAttachment (slotIndex:int, name:String, attachment:Attachment) : void { + if (attachment == null) + throw new ArgumentError("attachment cannot be null."); + attachments[slotIndex + ":" + name] = attachment; + } + + /** @return May be null. */ + public function getAttachment (slotIndex:int, name:String) : Attachment { + return attachments[slotIndex + ":" + name]; + } + + public function get name () : String { + return _name; + } + + public function toString () : String { + return _name; + } + + /** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */ + public function attachAll (skeleton:Skeleton, oldSkin:Skin) : void { + for each (var key:String in oldSkin.attachments) { + var colon:int = key.indexOf(":"); + var slotIndex:int = parseInt(key.substring(0, colon)); + var name:String = key.substring(colon + 1); + var slot:Slot = skeleton.slots[slotIndex]; + if (slot.attachment.name == name) { + var attachment:Attachment = getAttachment(slotIndex, name); + if (attachment != null) + slot.attachment = attachment; + } + } + } +} + +} diff --git a/spine-as3/src/spine/Slot.as b/spine-as3/src/spine/Slot.as new file mode 100644 index 000000000..61063aa14 --- /dev/null +++ b/spine-as3/src/spine/Slot.as @@ -0,0 +1,75 @@ +package spine { +import spine.attachments.Attachment; + +public class Slot { + internal var _data:SlotData; + internal var _bone:Bone; + internal var _skeleton:Skeleton; + public var r:Number; + public var g:Number; + public var b:Number; + public var a:Number; + internal var _attachment:Attachment; + private var _attachmentTime:Number; + + public function Slot (data:SlotData, skeleton:Skeleton, bone:Bone) { + if (data == null) + throw new ArgumentError("data cannot be null."); + if (skeleton == null) + throw new ArgumentError("skeleton cannot be null."); + if (bone == null) + throw new ArgumentError("bone cannot be null."); + _data = data; + _skeleton = skeleton; + _bone = bone; + setToBindPose(); + } + + public function get data () : SlotData { + return _data; + } + + public function get skeleton () : Skeleton { + return _skeleton; + } + + public function get bone () : Bone { + return _bone; + } + + /** @return May be null. */ + public function get attachment () : Attachment { + return _attachment; + } + + /** Sets the attachment and resets {@link #getAttachmentTime()}. + * @param attachment May be null. */ + public function set attachment (attachment:Attachment) : void { + _attachment = attachment; + _attachmentTime = _skeleton.time; + } + + public function set attachmentTime (time:Number) : void { + _attachmentTime = skeleton.time - time; + } + + /** Returns the time since the attachment was set. */ + public function get attachmentTime () : Number { + return skeleton.time - _attachmentTime; + } + + public function setToBindPose () : void { + var slotIndex:int = skeleton.data.slots.indexOf(data); + r = _data.r; + g = _data.g; + b = _data.b; + a = _data.a; + attachment = _data.attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slotIndex, data.attachmentName); + } + + public function toString () : String { + return _data.name; + } +} + +} diff --git a/spine-as3/src/spine/SlotData.as b/spine-as3/src/spine/SlotData.as new file mode 100644 index 000000000..d5884993e --- /dev/null +++ b/spine-as3/src/spine/SlotData.as @@ -0,0 +1,34 @@ +package spine { + +public class SlotData { + internal var _name:String; + internal var _boneData:BoneData; + public var r:Number = 1; + public var g:Number = 1; + public var b:Number = 1; + public var a:Number = 1; + public var attachmentName:String; + + public function SlotData (name:String, boneData:BoneData) { + if (name == null) + throw new ArgumentError("name cannot be null."); + if (boneData == null) + throw new ArgumentError("boneData cannot be null."); + _name = name; + _boneData = boneData; + } + + public function get name () : String { + return _name; + } + + public function get boneData () : BoneData { + return _boneData; + } + + public function toString () : String { + return _name; + } +} + +} diff --git a/spine-as3/src/spine/animation/Animation.as b/spine-as3/src/spine/animation/Animation.as new file mode 100644 index 000000000..ea8b890a6 --- /dev/null +++ b/spine-as3/src/spine/animation/Animation.as @@ -0,0 +1,83 @@ +package spine.animation { +import spine.Skeleton; + +public class Animation { + internal var _name:String; + private var _timelines:Vector.; + public var duration:Number; + + public function Animation (name:String, timelines:Vector., duration:Number) { + if (name == null) + throw new ArgumentError("name cannot be null."); + if (timelines == null) + throw new ArgumentError("timelines cannot be null."); + _name = name; + _timelines = timelines; + this.duration = duration; + } + + public function get timelines () : Vector. { + return _timelines; + } + + /** Poses the skeleton at the specified time for this animation. */ + public function apply (skeleton:Skeleton, time:Number, loop:Boolean) : void { + if (skeleton == null) + throw new ArgumentError("skeleton cannot be null."); + + if (loop && duration != 0) + time %= duration; + + for (var i:int = 0, n:int = timelines.length; i < n; i++) + timelines[i].apply(skeleton, time, 1); + } + + /** Poses the skeleton at the specified time for this animation mixed with the current pose. + * @param alpha The amount of this animation that affects the current pose. */ + public function mix (skeleton:Skeleton, time:Number, loop:Boolean, alpha:Number) : void { + if (skeleton == null) + throw new ArgumentError("skeleton cannot be null."); + + if (loop && duration != 0) + time %= duration; + + for (var i:int = 0, n:int = timelines.length; i < n; i++) + timelines[i].apply(skeleton, time, alpha); + } + + public function get name () : String { + return _name; + } + + public function toString () : String { + return _name; + } + + /** @param target After the first and before the last entry. */ + static public function binarySearch (values:Vector., target:Number, step:int) : int { + var low:int = 0; + var high:int = values.length / step - 2; + if (high == 0) + return step; + var current:int = high >>> 1; + while (true) { + if (values[(current + 1) * step] <= target) + low = current + 1; + else + high = current; + if (low == high) + return (low + 1) * step; + current = (low + high) >>> 1; + } + return 0; // Can't happen. + } + + static public function linearSearch (values:Vector., target:Number, step:int) : int { + for (var i:int = 0, last:int = values.length - step; i <= last; i += step) + if (values[i] > target) + return i; + return -1; + } +} + +} diff --git a/spine-as3/src/spine/animation/AttachmentTimeline.as b/spine-as3/src/spine/animation/AttachmentTimeline.as new file mode 100644 index 000000000..b33525c4d --- /dev/null +++ b/spine-as3/src/spine/animation/AttachmentTimeline.as @@ -0,0 +1,42 @@ +package spine.animation { +import spine.Skeleton; + +public class AttachmentTimeline implements Timeline { + public var slotIndex:int; + private var _frameCount:int; + public var frames:Vector. = new Vector.(); // time, ... + public var attachmentNames:Vector. = new Vector.(); + + public function AttachmentTimeline (frameCount:int) { + _frameCount = frameCount; + frames.length = frameCount; + attachmentNames = new String[frameCount]; + attachmentNames.length = frameCount; + } + + public function get frameCount () : int { + return _frameCount; + } + + /** Sets the time and value of the specified keyframe. */ + public function setFrame (frameIndex:int, time:Number, attachmentName:String) : void { + frames[frameIndex] = time; + attachmentNames[frameIndex] = attachmentName; + } + + public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + if (time < frames[0]) + return; // Time is before first frame. + + var frameIndex:int; + if (time >= frames[frames.length - 1]) // Time is after last frame. + frameIndex = frames.length - 1; + else + frameIndex = Animation.binarySearch(frames, time, 1) - 1; + + var attachmentName:String = attachmentNames[frameIndex]; + skeleton.slots[slotIndex].setAttachment(attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slotIndex, attachmentName)); + } +} + +} diff --git a/spine-as3/src/spine/animation/ColorTimeline.as b/spine-as3/src/spine/animation/ColorTimeline.as new file mode 100644 index 000000000..346c76ef7 --- /dev/null +++ b/spine-as3/src/spine/animation/ColorTimeline.as @@ -0,0 +1,73 @@ +package spine.animation { +import spine.Skeleton; +import spine.Slot; + +public class ColorTimeline extends CurveTimeline { + static private const LAST_FRAME_TIME:int = -5; + static private const FRAME_R:int = 1; + static private const FRAME_G:int = 2; + static private const FRAME_B:int = 3; + static private const FRAME_A:int = 4; + + public var slotIndex:int; + public var frames:Vector. = new Vector.(); // time, r, g, b, a, ... + + public function ColorTimeline (frameCount:int) { + super(frameCount); + frames.length = frameCount * 5; + } + + /** Sets the time and value of the specified keyframe. */ + public function setFrame (frameIndex:int, time:Number, r:Number, g:Number, b:Number, a:Number) : void { + frameIndex *= 5; + frames[frameIndex] = time; + frames[frameIndex + 1] = r; + frames[frameIndex + 2] = g; + frames[frameIndex + 3] = b; + frames[frameIndex + 4] = a; + } + + override public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + if (time < frames[0]) + return; // Time is before first frame. + + var slot:Slot = skeleton.slots[slotIndex]; + + if (time >= frames[frames.length - 5]) { // Time is after last frame. + var i:int = frames.length - 1; + slot.r = frames[i - 3]; + slot.g = frames[i - 2]; + slot.b = frames[i - 1]; + slot.a = frames[i]; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex:int = Animation.binarySearch(frames, time, 5); + var lastFrameR:Number = frames[frameIndex - 4]; + var lastFrameG:Number = frames[frameIndex - 3]; + var lastFrameB:Number = frames[frameIndex - 2]; + var lastFrameA:Number = frames[frameIndex - 1]; + var frameTime:Number = frames[frameIndex]; + var percent:Number = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime); + percent = getCurvePercent(frameIndex / 5 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + var r:Number = lastFrameR + (frames[frameIndex + FRAME_R] - lastFrameR) * percent; + var g:Number = lastFrameG + (frames[frameIndex + FRAME_G] - lastFrameG) * percent; + var b:Number = lastFrameB + (frames[frameIndex + FRAME_B] - lastFrameB) * percent; + var a:Number = lastFrameA + (frames[frameIndex + FRAME_A] - lastFrameA) * percent; + if (alpha < 1) { + slot.r += (r - slot.r) * alpha; + slot.g += (g - slot.g) * alpha; + slot.b += (b - slot.b) * alpha; + slot.a += (a - slot.a) * alpha; + } else { + slot.r = r; + slot.g = g; + slot.b = b; + slot.a = a; + } + } +} + +} diff --git a/spine-as3/src/spine/animation/CurveTimeline.as b/spine-as3/src/spine/animation/CurveTimeline.as new file mode 100644 index 000000000..19c6445cb --- /dev/null +++ b/spine-as3/src/spine/animation/CurveTimeline.as @@ -0,0 +1,92 @@ +package spine.animation { +import spine.Skeleton; + +/** Base class for frames that use an interpolation bezier curve. */ +public class CurveTimeline implements Timeline { + static private const LINEAR:Number = 0; + static private const STEPPED:Number = -1; + static private const BEZIER_SEGMENTS:int = 10; + + private var curves:Vector. = new Vector.(); // dfx, dfy, ddfx, ddfy, dddfx, dddfy, ... + private var _frameCount:int; + + public function CurveTimeline (frameCount:int) { + _frameCount = frameCount; + curves.length = frameCount * 6; + } + + public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + } + + public function get frameCount () : int { + return _frameCount; + } + + public function setLinear (frameIndex:int) : void { + curves[frameIndex * 6] = LINEAR; + } + + public function setStepped (frameIndex:int) : void { + curves[frameIndex * 6] = STEPPED; + } + + /** Sets the control handle positions for an interpolation bezier curve used to transition from this keyframe to the next. + * cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of + * the difference between the keyframe's values. */ + public function setCurve (frameIndex:int, cx1:Number, cy1:Number, cx2:Number, cy2:Number) : void { + var subdiv_step:Number = 1 / BEZIER_SEGMENTS; + var subdiv_step2:Number = subdiv_step * subdiv_step; + var subdiv_step3:Number = subdiv_step2 * subdiv_step; + var pre1:Number = 3 * subdiv_step; + var pre2:Number = 3 * subdiv_step2; + var pre4:Number = 6 * subdiv_step2; + var pre5:Number = 6 * subdiv_step3; + var tmp1x:Number = -cx1 * 2 + cx2; + var tmp1y:Number = -cy1 * 2 + cy2; + var tmp2x:Number = (cx1 - cx2) * 3 + 1; + var tmp2y:Number = (cy1 - cy2) * 3 + 1; + var i:int = frameIndex * 6; + curves[i] = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3; + curves[i + 1] = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3; + curves[i + 2] = tmp1x * pre4 + tmp2x * pre5; + curves[i + 3] = tmp1y * pre4 + tmp2y * pre5; + curves[i + 4] = tmp2x * pre5; + curves[i + 5] = tmp2y * pre5; + } + + public function getCurvePercent (frameIndex:int, percent:Number) : Number { + var curveIndex:int = frameIndex * 6; + var dfx:Number = curves[curveIndex]; + if (dfx == LINEAR) + return percent; + if (dfx == STEPPED) + return 0; + var dfy:Number = curves[curveIndex + 1]; + var ddfx:Number = curves[curveIndex + 2]; + var ddfy:Number = curves[curveIndex + 3]; + var dddfx:Number = curves[curveIndex + 4]; + var dddfy:Number = curves[curveIndex + 5]; + var x:Number = dfx; + var y:Number = dfy; + var i:int = BEZIER_SEGMENTS - 2; + while (true) { + if (x >= percent) { + var lastX:Number = x - dfx; + var lastY:Number = y - dfy; + return lastY + (y - lastY) * (percent - lastX) / (x - lastX); + } + if (i == 0) + break; + i--; + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + x += dfx; + y += dfy; + } + return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1. + } +} + +} diff --git a/spine-as3/src/spine/animation/RotateTimeline.as b/spine-as3/src/spine/animation/RotateTimeline.as new file mode 100644 index 000000000..bf7247c4c --- /dev/null +++ b/spine-as3/src/spine/animation/RotateTimeline.as @@ -0,0 +1,62 @@ +package spine.animation { +import spine.Bone; +import spine.Skeleton; + +public class RotateTimeline extends CurveTimeline { + static private const LAST_FRAME_TIME:int = -2; + static private const FRAME_VALUE:int = 1; + + public var boneIndex:int; + public var frames:Vector. = new Vector.(); // time, value, ... + + public function RotateTimeline (frameCount:int) { + super(frameCount); + frames.length = frameCount * 2; + } + + /** Sets the time and angle of the specified keyframe. */ + public function setFrame (frameIndex:int, time:Number, angle:Number) : void { + frameIndex *= 2; + frames[frameIndex] = time; + frames[frameIndex + 1] = angle; + } + + override public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + if (time < frames[0]) + return; // Time is before first frame. + + var bone:Bone = skeleton.bones[boneIndex]; + + var amount:Number; + if (time >= frames[frames.length - 2]) { // Time is after last frame. + amount = bone.data.rotation + frames[frames.length - 1] - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex:int = Animation.binarySearch(frames, time, 2); + var lastFrameValue:Number = frames[frameIndex - 1]; + var frameTime:Number = frames[frameIndex]; + var percent:Number = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime); + percent = getCurvePercent(frameIndex / 2 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + amount = frames[frameIndex + FRAME_VALUE] - lastFrameValue; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + amount = bone.data.rotation + (lastFrameValue + amount * percent) - bone.rotation; + while (amount > 180) + amount -= 360; + while (amount < -180) + amount += 360; + bone.rotation += amount * alpha; + } +} + +} diff --git a/spine-as3/src/spine/animation/ScaleTimeline.as b/spine-as3/src/spine/animation/ScaleTimeline.as new file mode 100644 index 000000000..c0c7a6ca9 --- /dev/null +++ b/spine-as3/src/spine/animation/ScaleTimeline.as @@ -0,0 +1,34 @@ +package spine.animation { +import spine.Bone; +import spine.Skeleton; + +public class ScaleTimeline extends TranslateTimeline { + public function ScaleTimeline (frameCount:int) { + super(frameCount); + } + + override public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + if (time < frames[0]) + return; // Time is before first frame. + + var bone:Bone = skeleton.bones[boneIndex]; + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.scaleX += (bone.data.scaleX - 1 + frames[frames.length - 2] - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY - 1 + frames[frames.length - 1] - bone.scaleY) * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex:int = Animation.binarySearch(frames, time, 3); + var lastFrameX:Number = frames[frameIndex - 2]; + var lastFrameY:Number = frames[frameIndex - 1]; + var frameTime:Number = frames[frameIndex]; + var percent:Number = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime); + percent = getCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + bone.scaleX += (bone.data.scaleX - 1 + lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent - bone.scaleX) * alpha; + bone.scaleY += (bone.data.scaleY - 1 + lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent - bone.scaleY) * alpha; + } +} + +} diff --git a/spine-as3/src/spine/animation/Timeline.as b/spine-as3/src/spine/animation/Timeline.as new file mode 100644 index 000000000..06db0f994 --- /dev/null +++ b/spine-as3/src/spine/animation/Timeline.as @@ -0,0 +1,9 @@ +package spine.animation { +import spine.Skeleton; + +public interface Timeline { + /** Sets the value(s) for the specified time. */ + function apply (skeleton:Skeleton, time:Number, alpha:Number) : void; +} + +} diff --git a/spine-as3/src/spine/animation/TranslateTimeline.as b/spine-as3/src/spine/animation/TranslateTimeline.as new file mode 100644 index 000000000..2fbb3a3b2 --- /dev/null +++ b/spine-as3/src/spine/animation/TranslateTimeline.as @@ -0,0 +1,51 @@ +package spine.animation { +import spine.Bone; +import spine.Skeleton; + +public class TranslateTimeline extends CurveTimeline { + static internal const LAST_FRAME_TIME:int = -3; + static internal const FRAME_X:int = 1; + static internal const FRAME_Y:int = 2; + + public var boneIndex:int; + public var frames:Vector. = new Vector.(); // time, value, value, ... + + public function TranslateTimeline (frameCount:int) { + super(frameCount); + frames.length = frameCount * 3; + } + + /** Sets the time and value of the specified keyframe. */ + public function setFrame (frameIndex:int, time:Number, x:Number, y:Number) : void { + frameIndex *= 3; + frames[frameIndex] = time; + frames[frameIndex + 1] = x; + frames[frameIndex + 2] = y; + } + + override public function apply (skeleton:Skeleton, time:Number, alpha:Number) : void { + if (time < frames[0]) + return; // Time is before first frame. + + var bone:Bone = skeleton.bones[boneIndex]; + + if (time >= frames[frames.length - 3]) { // Time is after last frame. + bone.x += (bone.data.x + frames[frames.length - 2] - bone.x) * alpha; + bone.y += (bone.data.y + frames[frames.length - 1] - bone.y) * alpha; + return; + } + + // Interpolate between the last frame and the current frame. + var frameIndex:int = Animation.binarySearch(frames, time, 3); + var lastFrameX:Number = frames[frameIndex - 2]; + var lastFrameY:Number = frames[frameIndex - 1]; + var frameTime:Number = frames[frameIndex]; + var percent:Number = 1 - (time - frameTime) / (frames[frameIndex + LAST_FRAME_TIME] - frameTime); + percent = getCurvePercent(frameIndex / 3 - 1, percent < 0 ? 0 : (percent > 1 ? 1 : percent)); + + bone.x += (bone.data.x + lastFrameX + (frames[frameIndex + FRAME_X] - lastFrameX) * percent - bone.x) * alpha; + bone.y += (bone.data.y + lastFrameY + (frames[frameIndex + FRAME_Y] - lastFrameY) * percent - bone.y) * alpha; + } +} + +} diff --git a/spine-as3/src/spine/atlas/AtlasPage.as b/spine-as3/src/spine/atlas/AtlasPage.as new file mode 100644 index 000000000..5a1e73c8d --- /dev/null +++ b/spine-as3/src/spine/atlas/AtlasPage.as @@ -0,0 +1,15 @@ +package spine.atlas { + +public class AtlasPage { + public var name:String; + public var format:Format; + public var minFilter:TextureFilter; + public var magFilter:TextureFilter; + public var uWrap:TextureWrap; + public var vWrap:TextureWrap; + public var texture:Object; + public var width:int; + public var height:int; +} + +} diff --git a/spine-as3/src/spine/atlas/AtlasRegion.as b/spine-as3/src/spine/atlas/AtlasRegion.as new file mode 100644 index 000000000..01c9d8c73 --- /dev/null +++ b/spine-as3/src/spine/atlas/AtlasRegion.as @@ -0,0 +1,24 @@ +package spine.atlas { + +public class AtlasRegion { + public var page:AtlasPage; + public var name:String; + public var x:int; + public var y:int; + public var width:int; + public var height:int; + public var u:Number; + public var v:Number; + public var u2:Number; + public var v2:Number; + public var offsetX:Number; + public var offsetY:Number; + public var originalWidth:int; + public var originalHeight:int; + public var index:int; + public var rotate:Boolean; + public var splits:Vector.; + public var pads:Vector.; +} + +} diff --git a/spine-as3/src/spine/atlas/Format.as b/spine-as3/src/spine/atlas/Format.as new file mode 100644 index 000000000..287a7d206 --- /dev/null +++ b/spine-as3/src/spine/atlas/Format.as @@ -0,0 +1,21 @@ +package spine.atlas { + +public class Format { + public static const alpha:Format = new Format(0, "alpha"); + public static const intensity:Format = new Format(1, "intensity"); + public static const luminanceAlpha:Format = new Format(2, "luminanceAlpha"); + public static const rgb565:Format = new Format(3, "rgb565"); + public static const rgba4444:Format = new Format(4, "rgba4444"); + public static const rgb888:Format = new Format(5, "rgb888"); + public static const rgba8888:Format = new Format(6, "rgba8888"); + + public var ordinal:int; + public var name:String; + + public function Format (ordinal:int, name:String) { + this.ordinal = ordinal; + this.name = name; + } +} + +} diff --git a/spine-as3/src/spine/atlas/TextureFilter.as b/spine-as3/src/spine/atlas/TextureFilter.as new file mode 100644 index 000000000..ad8e398d3 --- /dev/null +++ b/spine-as3/src/spine/atlas/TextureFilter.as @@ -0,0 +1,21 @@ +package spine.atlas { + +public class TextureFilter { + public static const nearest:TextureFilter = new TextureFilter(0, "nearest"); + public static const linear:TextureFilter = new TextureFilter(1, "linear"); + public static const mipMap:TextureFilter = new TextureFilter(2, "mipMap"); + public static const mipMapNearestNearest:TextureFilter = new TextureFilter(3, "mipMapNearestNearest"); + public static const mipMapLinearNearest:TextureFilter = new TextureFilter(4, "mipMapLinearNearest"); + public static const mipMapNearestLinear:TextureFilter = new TextureFilter(5, "mipMapNearestLinear"); + public static const mipMapLinearLinear:TextureFilter = new TextureFilter(6, "mipMapLinearLinear"); + + public var ordinal:int; + public var name:String; + + public function TextureFilter (ordinal:int, name:String) { + this.ordinal = ordinal; + this.name = name; + } +} + +} diff --git a/spine-as3/src/spine/atlas/TextureWrap.as b/spine-as3/src/spine/atlas/TextureWrap.as new file mode 100644 index 000000000..79f6ecd4f --- /dev/null +++ b/spine-as3/src/spine/atlas/TextureWrap.as @@ -0,0 +1,17 @@ +package spine.atlas { + +public class TextureWrap { + public static const mirroredRepeat:TextureWrap = new TextureWrap(0, "mirroredRepeat"); + public static const clampToEdge:TextureWrap = new TextureWrap(1, "clampToEdge"); + public static const repeat:TextureWrap = new TextureWrap(2, "repeat"); + + public var ordinal:int; + public var name:String; + + public function TextureWrap (ordinal:int, name:String) { + this.ordinal = ordinal; + this.name = name; + } +} + +} diff --git a/spine-as3/src/spine/attachments/Attachment.as b/spine-as3/src/spine/attachments/Attachment.as new file mode 100644 index 000000000..2655c6fd7 --- /dev/null +++ b/spine-as3/src/spine/attachments/Attachment.as @@ -0,0 +1,21 @@ +package spine.attachments { + +public class Attachment { + internal var _name:String; + + public function Attachment (name:String) { + if (name == null) + throw new ArgumentError("name cannot be null."); + _name = name; + } + + public function get name () : String { + return _name; + } + + public function toString () : String { + return name; + } +} + +} diff --git a/spine-as3/src/spine/attachments/AttachmentLoader.as b/spine-as3/src/spine/attachments/AttachmentLoader.as new file mode 100644 index 000000000..437c90470 --- /dev/null +++ b/spine-as3/src/spine/attachments/AttachmentLoader.as @@ -0,0 +1,9 @@ +package spine.attachments { +import spine.Skin; + +public interface AttachmentLoader { + /** @return May be null to not load an attachment. */ + function newAttachment (skin:Skin, type:AttachmentType, name:String) : Attachment; +} + +} diff --git a/spine-as3/src/spine/attachments/AttachmentType.as b/spine-as3/src/spine/attachments/AttachmentType.as new file mode 100644 index 000000000..e7a2d6448 --- /dev/null +++ b/spine-as3/src/spine/attachments/AttachmentType.as @@ -0,0 +1,26 @@ +package spine.attachments { + +public class AttachmentType { + public static const region:AttachmentType = new AttachmentType(0, "region"); + public static const regionSequence:AttachmentType = new AttachmentType(1, "regionSequence"); + + public var ordinal:int; + public var name:String; + + public function AttachmentType (ordinal:int, name:String) { + this.ordinal = ordinal; + this.name = name; + } + + static public function valueOf (name:String) : AttachmentType { + switch (name) { + case "region": + return region; + case "regionSequence": + return regionSequence; + } + return null; + } +} + +} diff --git a/spine-as3/src/spine/attachments/RegionAttachment.as b/spine-as3/src/spine/attachments/RegionAttachment.as new file mode 100644 index 000000000..72d0de5c6 --- /dev/null +++ b/spine-as3/src/spine/attachments/RegionAttachment.as @@ -0,0 +1,106 @@ +package spine.attachments { +import spine.Bone; + +public class RegionAttachment extends Attachment { + public const X1:int = 0; + public const Y1:int = 1; + public const X2:int = 2; + public const Y2:int = 3; + public const X3:int = 4; + public const Y3:int = 5; + public const X4:int = 6; + public const Y4:int = 7; + + public var x:Number; + public var y:Number; + public var scaleX:Number = 1; + public var scaleY:Number = 1; + public var rotation:Number; + public var width:Number; + public var height:Number; + + public var texture:Object; + public var regionOffsetX:Number; // Pixels stripped from the bottom left, unrotated. + public var regionOffsetY:Number; + public var regionWidth:Number; // Unrotated, stripped size. + public var regionHeight:Number; + public var regionOriginalWidth:Number; // Unrotated, unstripped size. + public var regionOriginalHeight:Number; + + public var vertices:Vector. = new Vector.(); + public var offset:Vector. = new Vector.(); + public var uvs:Vector. = new Vector.(); + + public function RegionAttachment (name:String) { + super(name); + } + + public function setUVs (u:Number, v:Number, u2:Number, v2:Number, rotate:Boolean) : void { + if (rotate) { + uvs[X2] = u; + uvs[Y2] = v2; + uvs[X3] = u; + uvs[Y3] = v; + uvs[X4] = u2; + uvs[Y4] = v; + uvs[X1] = u2; + uvs[Y1] = v2; + } else { + uvs[X1] = u; + uvs[Y1] = v2; + uvs[X2] = u; + uvs[Y2] = v; + uvs[X3] = u2; + uvs[Y3] = v; + uvs[X4] = u2; + uvs[Y4] = v2; + } + } + + public function updateOffset () : void { + var regionScaleX:Number = width / regionOriginalWidth * scaleX; + var regionScaleY:Number = height / regionOriginalHeight * scaleY; + var localX:Number = -width / 2 * scaleX + regionOffsetX * regionScaleX; + var localY:Number = -height / 2 * scaleY + regionOffsetY * regionScaleY; + var localX2:Number = localX + regionWidth * regionScaleX; + var localY2:Number = localY + regionHeight * regionScaleY; + var radians:Number = rotation * Math.PI / 180; + var cos:Number = Math.cos(radians); + var sin:Number = Math.sin(radians); + var localXCos:Number = localX * cos + x; + var localXSin:Number = localX * sin; + var localYCos:Number = localY * cos + y; + var localYSin:Number = localY * sin; + var localX2Cos:Number = localX2 * cos + x; + var localX2Sin:Number = localX2 * sin; + var localY2Cos:Number = localY2 * cos + y; + var localY2Sin:Number = localY2 * sin; + offset[X1] = localXCos - localYSin; + offset[Y1] = localYCos + localXSin; + offset[X2] = localXCos - localY2Sin; + offset[Y2] = localY2Cos + localXSin; + offset[X3] = localX2Cos - localY2Sin; + offset[Y3] = localY2Cos + localX2Sin; + offset[X4] = localX2Cos - localYSin; + offset[Y4] = localYCos + localX2Sin; + } + + public function updateVertices (bone:Bone) : void { + var x:Number = bone.worldX; + var y:Number = bone.worldY; + var m00:Number = bone.m00; + var m01:Number = bone.m01; + var m10:Number = bone.m10; + var m11:Number = bone.m11; + vertices[X1] = offset[X1] * m00 + offset[Y1] * m01 + x; + vertices[Y1] = offset[X1] * m10 + offset[Y1] * m11 + y; + vertices[X2] = offset[X2] * m00 + offset[Y2] * m01 + x; + vertices[Y2] = offset[X2] * m10 + offset[Y2] * m11 + y; + vertices[X3] = offset[X3] * m00 + offset[Y3] * m01 + x; + vertices[Y3] = offset[X3] * m10 + offset[Y3] * m11 + y; + vertices[X4] = offset[X4] * m00 + offset[Y4] * m01 + x; + vertices[Y4] = offset[X4] * m10 + offset[Y4] * m11 + y; + } +} + +} diff --git a/spine-starling/spine-starling-example/.actionScriptProperties b/spine-starling/spine-starling-example/.actionScriptProperties new file mode 100644 index 000000000..9ed01a488 --- /dev/null +++ b/spine-starling/spine-starling-example/.actionScriptProperties @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spine-starling/spine-starling-example/.project b/spine-starling/spine-starling-example/.project new file mode 100644 index 000000000..dc21b1cdd --- /dev/null +++ b/spine-starling/spine-starling-example/.project @@ -0,0 +1,17 @@ + + + spine-starling-example + + + + + + com.adobe.flexbuilder.project.flexbuilder + + + + + + com.adobe.flexbuilder.project.actionscriptnature + + diff --git a/spine-starling/spine-starling-example/.settings/org.eclipse.core.resources.prefs b/spine-starling/spine-starling-example/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..1c746094a --- /dev/null +++ b/spine-starling/spine-starling-example/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Tue Apr 30 19:02:42 CEST 2013 +eclipse.preferences.version=1 +encoding/=utf-8 diff --git a/spine-starling/spine-starling-example/html-template/history/history.css b/spine-starling/spine-starling-example/html-template/history/history.css new file mode 100644 index 000000000..dbc47c61b --- /dev/null +++ b/spine-starling/spine-starling-example/html-template/history/history.css @@ -0,0 +1,6 @@ +/* This CSS stylesheet defines styles used by required elements in a flex application page that supports browser history */ + +#ie_historyFrame { width: 0px; height: 0px; display:none } +#firefox_anchorDiv { width: 0px; height: 0px; display:none } +#safari_formDiv { width: 0px; height: 0px; display:none } +#safari_rememberDiv { width: 0px; height: 0px; display:none } diff --git a/spine-starling/spine-starling-example/html-template/history/history.js b/spine-starling/spine-starling-example/html-template/history/history.js new file mode 100644 index 000000000..00a8bfee7 --- /dev/null +++ b/spine-starling/spine-starling-example/html-template/history/history.js @@ -0,0 +1,678 @@ +BrowserHistoryUtils = { + addEvent: function(elm, evType, fn, useCapture) { + useCapture = useCapture || false; + if (elm.addEventListener) { + elm.addEventListener(evType, fn, useCapture); + return true; + } + else if (elm.attachEvent) { + var r = elm.attachEvent('on' + evType, fn); + return r; + } + else { + elm['on' + evType] = fn; + } + } +} + +BrowserHistory = (function() { + // type of browser + var browser = { + ie: false, + ie8: false, + firefox: false, + safari: false, + opera: false, + version: -1 + }; + + // Default app state URL to use when no fragment ID present + var defaultHash = ''; + + // Last-known app state URL + var currentHref = document.location.href; + + // Initial URL (used only by IE) + var initialHref = document.location.href; + + // Initial URL (used only by IE) + var initialHash = document.location.hash; + + // History frame source URL prefix (used only by IE) + var historyFrameSourcePrefix = 'history/historyFrame.html?'; + + // History maintenance (used only by Safari) + var currentHistoryLength = -1; + + // Flag to denote the existence of onhashchange + var browserHasHashChange = false; + + var historyHash = []; + + var initialState = createState(initialHref, initialHref + '#' + initialHash, initialHash); + + var backStack = []; + var forwardStack = []; + + var currentObjectId = null; + + //UserAgent detection + var useragent = navigator.userAgent.toLowerCase(); + + if (useragent.indexOf("opera") != -1) { + browser.opera = true; + } else if (useragent.indexOf("msie") != -1) { + browser.ie = true; + browser.version = parseFloat(useragent.substring(useragent.indexOf('msie') + 4)); + if (browser.version == 8) + { + browser.ie = false; + browser.ie8 = true; + } + } else if (useragent.indexOf("safari") != -1) { + browser.safari = true; + browser.version = parseFloat(useragent.substring(useragent.indexOf('safari') + 7)); + } else if (useragent.indexOf("gecko") != -1) { + browser.firefox = true; + } + + if (browser.ie == true && browser.version == 7) { + window["_ie_firstload"] = false; + } + + function hashChangeHandler() + { + currentHref = document.location.href; + var flexAppUrl = getHash(); + //ADR: to fix multiple + if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { + var pl = getPlayers(); + for (var i = 0; i < pl.length; i++) { + pl[i].browserURLChange(flexAppUrl); + } + } else { + getPlayer().browserURLChange(flexAppUrl); + } + } + + // Accessor functions for obtaining specific elements of the page. + function getHistoryFrame() + { + return document.getElementById('ie_historyFrame'); + } + + function getFormElement() + { + return document.getElementById('safari_formDiv'); + } + + function getRememberElement() + { + return document.getElementById("safari_remember_field"); + } + + // Get the Flash player object for performing ExternalInterface callbacks. + // Updated for changes to SWFObject2. + function getPlayer(id) { + var i; + + if (id && document.getElementById(id)) { + var r = document.getElementById(id); + if (typeof r.SetVariable != "undefined") { + return r; + } + else { + var o = r.getElementsByTagName("object"); + var e = r.getElementsByTagName("embed"); + for (i = 0; i < o.length; i++) { + if (typeof o[i].browserURLChange != "undefined") + return o[i]; + } + for (i = 0; i < e.length; i++) { + if (typeof e[i].browserURLChange != "undefined") + return e[i]; + } + } + } + else { + var o = document.getElementsByTagName("object"); + var e = document.getElementsByTagName("embed"); + for (i = 0; i < e.length; i++) { + if (typeof e[i].browserURLChange != "undefined") + { + return e[i]; + } + } + for (i = 0; i < o.length; i++) { + if (typeof o[i].browserURLChange != "undefined") + { + return o[i]; + } + } + } + return undefined; + } + + function getPlayers() { + var i; + var players = []; + if (players.length == 0) { + var tmp = document.getElementsByTagName('object'); + for (i = 0; i < tmp.length; i++) + { + if (typeof tmp[i].browserURLChange != "undefined") + players.push(tmp[i]); + } + } + if (players.length == 0 || players[0].object == null) { + var tmp = document.getElementsByTagName('embed'); + for (i = 0; i < tmp.length; i++) + { + if (typeof tmp[i].browserURLChange != "undefined") + players.push(tmp[i]); + } + } + return players; + } + + function getIframeHash() { + var doc = getHistoryFrame().contentWindow.document; + var hash = String(doc.location.search); + if (hash.length == 1 && hash.charAt(0) == "?") { + hash = ""; + } + else if (hash.length >= 2 && hash.charAt(0) == "?") { + hash = hash.substring(1); + } + return hash; + } + + /* Get the current location hash excluding the '#' symbol. */ + function getHash() { + // It would be nice if we could use document.location.hash here, + // but it's faulty sometimes. + var idx = document.location.href.indexOf('#'); + return (idx >= 0) ? document.location.href.substr(idx+1) : ''; + } + + /* Get the current location hash excluding the '#' symbol. */ + function setHash(hash) { + // It would be nice if we could use document.location.hash here, + // but it's faulty sometimes. + if (hash == '') hash = '#' + document.location.hash = hash; + } + + function createState(baseUrl, newUrl, flexAppUrl) { + return { 'baseUrl': baseUrl, 'newUrl': newUrl, 'flexAppUrl': flexAppUrl, 'title': null }; + } + + /* Add a history entry to the browser. + * baseUrl: the portion of the location prior to the '#' + * newUrl: the entire new URL, including '#' and following fragment + * flexAppUrl: the portion of the location following the '#' only + */ + function addHistoryEntry(baseUrl, newUrl, flexAppUrl) { + + //delete all the history entries + forwardStack = []; + + if (browser.ie) { + //Check to see if we are being asked to do a navigate for the first + //history entry, and if so ignore, because it's coming from the creation + //of the history iframe + if (flexAppUrl == defaultHash && document.location.href == initialHref && window['_ie_firstload']) { + currentHref = initialHref; + return; + } + if ((!flexAppUrl || flexAppUrl == defaultHash) && window['_ie_firstload']) { + newUrl = baseUrl + '#' + defaultHash; + flexAppUrl = defaultHash; + } else { + // for IE, tell the history frame to go somewhere without a '#' + // in order to get this entry into the browser history. + getHistoryFrame().src = historyFrameSourcePrefix + flexAppUrl; + } + setHash(flexAppUrl); + } else { + + //ADR + if (backStack.length == 0 && initialState.flexAppUrl == flexAppUrl) { + initialState = createState(baseUrl, newUrl, flexAppUrl); + } else if(backStack.length > 0 && backStack[backStack.length - 1].flexAppUrl == flexAppUrl) { + backStack[backStack.length - 1] = createState(baseUrl, newUrl, flexAppUrl); + } + + if (browser.safari && !browserHasHashChange) { + // for Safari, submit a form whose action points to the desired URL + if (browser.version <= 419.3) { + var file = window.location.pathname.toString(); + file = file.substring(file.lastIndexOf("/")+1); + getFormElement().innerHTML = '
'; + //get the current elements and add them to the form + var qs = window.location.search.substring(1); + var qs_arr = qs.split("&"); + for (var i = 0; i < qs_arr.length; i++) { + var tmp = qs_arr[i].split("="); + var elem = document.createElement("input"); + elem.type = "hidden"; + elem.name = tmp[0]; + elem.value = tmp[1]; + document.forms.historyForm.appendChild(elem); + } + document.forms.historyForm.submit(); + } else { + top.location.hash = flexAppUrl; + } + // We also have to maintain the history by hand for Safari + historyHash[history.length] = flexAppUrl; + _storeStates(); + } else { + // Otherwise, just tell the browser to go there + setHash(flexAppUrl); + } + } + backStack.push(createState(baseUrl, newUrl, flexAppUrl)); + } + + function _storeStates() { + if (browser.safari) { + getRememberElement().value = historyHash.join(","); + } + } + + function handleBackButton() { + //The "current" page is always at the top of the history stack. + var current = backStack.pop(); + if (!current) { return; } + var last = backStack[backStack.length - 1]; + if (!last && backStack.length == 0){ + last = initialState; + } + forwardStack.push(current); + } + + function handleForwardButton() { + //summary: private method. Do not call this directly. + + var last = forwardStack.pop(); + if (!last) { return; } + backStack.push(last); + } + + function handleArbitraryUrl() { + //delete all the history entries + forwardStack = []; + } + + /* Called periodically to poll to see if we need to detect navigation that has occurred */ + function checkForUrlChange() { + + if (browser.ie) { + if (currentHref != document.location.href && currentHref + '#' != document.location.href) { + //This occurs when the user has navigated to a specific URL + //within the app, and didn't use browser back/forward + //IE seems to have a bug where it stops updating the URL it + //shows the end-user at this point, but programatically it + //appears to be correct. Do a full app reload to get around + //this issue. + if (browser.version < 7) { + currentHref = document.location.href; + document.location.reload(); + } else { + if (getHash() != getIframeHash()) { + // this.iframe.src = this.blankURL + hash; + var sourceToSet = historyFrameSourcePrefix + getHash(); + getHistoryFrame().src = sourceToSet; + currentHref = document.location.href; + } + } + } + } + + if (browser.safari && !browserHasHashChange) { + // For Safari, we have to check to see if history.length changed. + if (currentHistoryLength >= 0 && history.length != currentHistoryLength) { + //alert("did change: " + history.length + ", " + historyHash.length + "|" + historyHash[history.length] + "|>" + historyHash.join("|")); + var flexAppUrl = getHash(); + if (browser.version < 528.16 /* Anything earlier than Safari 4.0 */) + { + // If it did change and we're running Safari 3.x or earlier, + // then we have to look the old state up in our hand-maintained + // array since document.location.hash won't have changed, + // then call back into BrowserManager. + currentHistoryLength = history.length; + flexAppUrl = historyHash[currentHistoryLength]; + } + + //ADR: to fix multiple + if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { + var pl = getPlayers(); + for (var i = 0; i < pl.length; i++) { + pl[i].browserURLChange(flexAppUrl); + } + } else { + getPlayer().browserURLChange(flexAppUrl); + } + _storeStates(); + } + } + if (browser.firefox && !browserHasHashChange) { + if (currentHref != document.location.href) { + var bsl = backStack.length; + + var urlActions = { + back: false, + forward: false, + set: false + } + + if ((window.location.hash == initialHash || window.location.href == initialHref) && (bsl == 1)) { + urlActions.back = true; + // FIXME: could this ever be a forward button? + // we can't clear it because we still need to check for forwards. Ugg. + // clearInterval(this.locationTimer); + handleBackButton(); + } + + // first check to see if we could have gone forward. We always halt on + // a no-hash item. + if (forwardStack.length > 0) { + if (forwardStack[forwardStack.length-1].flexAppUrl == getHash()) { + urlActions.forward = true; + handleForwardButton(); + } + } + + // ok, that didn't work, try someplace back in the history stack + if ((bsl >= 2) && (backStack[bsl - 2])) { + if (backStack[bsl - 2].flexAppUrl == getHash()) { + urlActions.back = true; + handleBackButton(); + } + } + + if (!urlActions.back && !urlActions.forward) { + var foundInStacks = { + back: -1, + forward: -1 + } + + for (var i = 0; i < backStack.length; i++) { + if (backStack[i].flexAppUrl == getHash() && i != (bsl - 2)) { + arbitraryUrl = true; + foundInStacks.back = i; + } + } + for (var i = 0; i < forwardStack.length; i++) { + if (forwardStack[i].flexAppUrl == getHash() && i != (bsl - 2)) { + arbitraryUrl = true; + foundInStacks.forward = i; + } + } + handleArbitraryUrl(); + } + + // Firefox changed; do a callback into BrowserManager to tell it. + currentHref = document.location.href; + var flexAppUrl = getHash(); + //ADR: to fix multiple + if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { + var pl = getPlayers(); + for (var i = 0; i < pl.length; i++) { + pl[i].browserURLChange(flexAppUrl); + } + } else { + getPlayer().browserURLChange(flexAppUrl); + } + } + } + } + + var _initialize = function () { + + browserHasHashChange = ("onhashchange" in document.body); + + if (browser.ie) + { + var scripts = document.getElementsByTagName('script'); + for (var i = 0, s; s = scripts[i]; i++) { + if (s.src.indexOf("history.js") > -1) { + var iframe_location = (new String(s.src)).replace("history.js", "historyFrame.html"); + } + } + historyFrameSourcePrefix = iframe_location + "?"; + var src = historyFrameSourcePrefix; + + var iframe = document.createElement("iframe"); + iframe.id = 'ie_historyFrame'; + iframe.name = 'ie_historyFrame'; + iframe.src = 'javascript:false;'; + + try { + document.body.appendChild(iframe); + } catch(e) { + setTimeout(function() { + document.body.appendChild(iframe); + }, 0); + } + } + + if (browser.safari && !browserHasHashChange) + { + var rememberDiv = document.createElement("div"); + rememberDiv.id = 'safari_rememberDiv'; + document.body.appendChild(rememberDiv); + rememberDiv.innerHTML = ''; + + var formDiv = document.createElement("div"); + formDiv.id = 'safari_formDiv'; + document.body.appendChild(formDiv); + + var reloader_content = document.createElement('div'); + reloader_content.id = 'safarireloader'; + var scripts = document.getElementsByTagName('script'); + for (var i = 0, s; s = scripts[i]; i++) { + if (s.src.indexOf("history.js") > -1) { + html = (new String(s.src)).replace(".js", ".html"); + } + } + reloader_content.innerHTML = ''; + document.body.appendChild(reloader_content); + reloader_content.style.position = 'absolute'; + reloader_content.style.left = reloader_content.style.top = '-9999px'; + iframe = reloader_content.getElementsByTagName('iframe')[0]; + + if (document.getElementById("safari_remember_field").value != "" ) { + historyHash = document.getElementById("safari_remember_field").value.split(","); + } + } + + if (browserHasHashChange) + document.body.onhashchange = hashChangeHandler; + } + + return { + historyHash: historyHash, + backStack: function() { return backStack; }, + forwardStack: function() { return forwardStack }, + getPlayer: getPlayer, + initialize: function(src) { + _initialize(src); + }, + setURL: function(url) { + document.location.href = url; + }, + getURL: function() { + return document.location.href; + }, + getTitle: function() { + return document.title; + }, + setTitle: function(title) { + try { + backStack[backStack.length - 1].title = title; + } catch(e) { } + //if on safari, set the title to be the empty string. + if (browser.safari) { + if (title == "") { + try { + var tmp = window.location.href.toString(); + title = tmp.substring((tmp.lastIndexOf("/")+1), tmp.lastIndexOf("#")); + } catch(e) { + title = ""; + } + } + } + document.title = title; + }, + setDefaultURL: function(def) + { + defaultHash = def; + def = getHash(); + //trailing ? is important else an extra frame gets added to the history + //when navigating back to the first page. Alternatively could check + //in history frame navigation to compare # and ?. + if (browser.ie) + { + window['_ie_firstload'] = true; + var sourceToSet = historyFrameSourcePrefix + def; + var func = function() { + getHistoryFrame().src = sourceToSet; + window.location.replace("#" + def); + setInterval(checkForUrlChange, 50); + } + try { + func(); + } catch(e) { + window.setTimeout(function() { func(); }, 0); + } + } + + if (browser.safari) + { + currentHistoryLength = history.length; + if (historyHash.length == 0) { + historyHash[currentHistoryLength] = def; + var newloc = "#" + def; + window.location.replace(newloc); + } else { + //alert(historyHash[historyHash.length-1]); + } + setInterval(checkForUrlChange, 50); + } + + + if (browser.firefox || browser.opera) + { + var reg = new RegExp("#" + def + "$"); + if (window.location.toString().match(reg)) { + } else { + var newloc ="#" + def; + window.location.replace(newloc); + } + setInterval(checkForUrlChange, 50); + } + + }, + + /* Set the current browser URL; called from inside BrowserManager to propagate + * the application state out to the container. + */ + setBrowserURL: function(flexAppUrl, objectId) { + if (browser.ie && typeof objectId != "undefined") { + currentObjectId = objectId; + } + //fromIframe = fromIframe || false; + //fromFlex = fromFlex || false; + //alert("setBrowserURL: " + flexAppUrl); + //flexAppUrl = (flexAppUrl == "") ? defaultHash : flexAppUrl ; + + var pos = document.location.href.indexOf('#'); + var baseUrl = pos != -1 ? document.location.href.substr(0, pos) : document.location.href; + var newUrl = baseUrl + '#' + flexAppUrl; + + if (document.location.href != newUrl && document.location.href + '#' != newUrl) { + currentHref = newUrl; + addHistoryEntry(baseUrl, newUrl, flexAppUrl); + currentHistoryLength = history.length; + } + }, + + browserURLChange: function(flexAppUrl) { + var objectId = null; + if (browser.ie && currentObjectId != null) { + objectId = currentObjectId; + } + + if (typeof BrowserHistory_multiple != "undefined" && BrowserHistory_multiple == true) { + var pl = getPlayers(); + for (var i = 0; i < pl.length; i++) { + try { + pl[i].browserURLChange(flexAppUrl); + } catch(e) { } + } + } else { + try { + getPlayer(objectId).browserURLChange(flexAppUrl); + } catch(e) { } + } + + currentObjectId = null; + }, + getUserAgent: function() { + return navigator.userAgent; + }, + getPlatform: function() { + return navigator.platform; + } + + } + +})(); + +// Initialization + +// Automated unit testing and other diagnostics + +function setURL(url) +{ + document.location.href = url; +} + +function backButton() +{ + history.back(); +} + +function forwardButton() +{ + history.forward(); +} + +function goForwardOrBackInHistory(step) +{ + history.go(step); +} + +//BrowserHistoryUtils.addEvent(window, "load", function() { BrowserHistory.initialize(); }); +(function(i) { + var u =navigator.userAgent;var e=/*@cc_on!@*/false; + var st = setTimeout; + if(/webkit/i.test(u)){ + st(function(){ + var dr=document.readyState; + if(dr=="loaded"||dr=="complete"){i()} + else{st(arguments.callee,10);}},10); + } else if((/mozilla/i.test(u)&&!/(compati)/.test(u)) || (/opera/i.test(u))){ + document.addEventListener("DOMContentLoaded",i,false); + } else if(e){ + (function(){ + var t=document.createElement('doc:rdy'); + try{t.doScroll('left'); + i();t=null; + }catch(e){st(arguments.callee,0);}})(); + } else{ + window.onload=i; + } +})( function() {BrowserHistory.initialize();} ); diff --git a/spine-starling/spine-starling-example/html-template/history/historyFrame.html b/spine-starling/spine-starling-example/html-template/history/historyFrame.html new file mode 100644 index 000000000..07e3806f0 --- /dev/null +++ b/spine-starling/spine-starling-example/html-template/history/historyFrame.html @@ -0,0 +1,29 @@ + + + + + + + + Hidden frame for Browser History support. + + diff --git a/spine-starling/spine-starling-example/html-template/index.template.html b/spine-starling/spine-starling-example/html-template/index.template.html new file mode 100644 index 000000000..3b89e4ee5 --- /dev/null +++ b/spine-starling/spine-starling-example/html-template/index.template.html @@ -0,0 +1,109 @@ + + + + + + ${title} + + + + + + + + + + + + + +
+

+ To view this page ensure that Adobe Flash Player version + ${version_major}.${version_minor}.${version_revision} or greater is installed. +

+ +
+ + + + diff --git a/spine-starling/spine-starling-example/html-template/playerProductInstall.swf b/spine-starling/spine-starling-example/html-template/playerProductInstall.swf new file mode 100644 index 000000000..bdc343785 Binary files /dev/null and b/spine-starling/spine-starling-example/html-template/playerProductInstall.swf differ diff --git a/spine-starling/spine-starling-example/html-template/swfobject.js b/spine-starling/spine-starling-example/html-template/swfobject.js new file mode 100644 index 000000000..bf35c07c8 --- /dev/null +++ b/spine-starling/spine-starling-example/html-template/swfobject.js @@ -0,0 +1,777 @@ +/*! SWFObject v2.2 + is released under the MIT License +*/ + +var swfobject = function() { + + var UNDEF = "undefined", + OBJECT = "object", + SHOCKWAVE_FLASH = "Shockwave Flash", + SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash", + FLASH_MIME_TYPE = "application/x-shockwave-flash", + EXPRESS_INSTALL_ID = "SWFObjectExprInst", + ON_READY_STATE_CHANGE = "onreadystatechange", + + win = window, + doc = document, + nav = navigator, + + plugin = false, + domLoadFnArr = [main], + regObjArr = [], + objIdArr = [], + listenersArr = [], + storedAltContent, + storedAltContentId, + storedCallbackFn, + storedCallbackObj, + isDomLoaded = false, + isExpressInstallActive = false, + dynamicStylesheet, + dynamicStylesheetMedia, + autoHideShow = true, + + /* Centralized function for browser feature detection + - User agent string detection is only used when no good alternative is possible + - Is executed directly for optimal performance + */ + ua = function() { + var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF, + u = nav.userAgent.toLowerCase(), + p = nav.platform.toLowerCase(), + windows = p ? /win/.test(p) : /win/.test(u), + mac = p ? /mac/.test(p) : /mac/.test(u), + webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit + ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html + playerVersion = [0,0,0], + d = null; + if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) { + d = nav.plugins[SHOCKWAVE_FLASH].description; + if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+ + plugin = true; + ie = false; // cascaded feature detection for Internet Explorer + d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1"); + playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10); + playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10); + playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0; + } + } + else if (typeof win.ActiveXObject != UNDEF) { + try { + var a = new ActiveXObject(SHOCKWAVE_FLASH_AX); + if (a) { // a will return null when ActiveX is disabled + d = a.GetVariable("$version"); + if (d) { + ie = true; // cascaded feature detection for Internet Explorer + d = d.split(" ")[1].split(","); + playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; + } + } + } + catch(e) {} + } + return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac }; + }(), + + /* Cross-browser onDomLoad + - Will fire an event as soon as the DOM of a web page is loaded + - Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/ + - Regular onload serves as fallback + */ + onDomLoad = function() { + if (!ua.w3) { return; } + if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically + callDomLoadFunctions(); + } + if (!isDomLoaded) { + if (typeof doc.addEventListener != UNDEF) { + doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false); + } + if (ua.ie && ua.win) { + doc.attachEvent(ON_READY_STATE_CHANGE, function() { + if (doc.readyState == "complete") { + doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee); + callDomLoadFunctions(); + } + }); + if (win == top) { // if not inside an iframe + (function(){ + if (isDomLoaded) { return; } + try { + doc.documentElement.doScroll("left"); + } + catch(e) { + setTimeout(arguments.callee, 0); + return; + } + callDomLoadFunctions(); + })(); + } + } + if (ua.wk) { + (function(){ + if (isDomLoaded) { return; } + if (!/loaded|complete/.test(doc.readyState)) { + setTimeout(arguments.callee, 0); + return; + } + callDomLoadFunctions(); + })(); + } + addLoadEvent(callDomLoadFunctions); + } + }(); + + function callDomLoadFunctions() { + if (isDomLoaded) { return; } + try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early + var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span")); + t.parentNode.removeChild(t); + } + catch (e) { return; } + isDomLoaded = true; + var dl = domLoadFnArr.length; + for (var i = 0; i < dl; i++) { + domLoadFnArr[i](); + } + } + + function addDomLoadEvent(fn) { + if (isDomLoaded) { + fn(); + } + else { + domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+ + } + } + + /* Cross-browser onload + - Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/ + - Will fire an event as soon as a web page including all of its assets are loaded + */ + function addLoadEvent(fn) { + if (typeof win.addEventListener != UNDEF) { + win.addEventListener("load", fn, false); + } + else if (typeof doc.addEventListener != UNDEF) { + doc.addEventListener("load", fn, false); + } + else if (typeof win.attachEvent != UNDEF) { + addListener(win, "onload", fn); + } + else if (typeof win.onload == "function") { + var fnOld = win.onload; + win.onload = function() { + fnOld(); + fn(); + }; + } + else { + win.onload = fn; + } + } + + /* Main function + - Will preferably execute onDomLoad, otherwise onload (as a fallback) + */ + function main() { + if (plugin) { + testPlayerVersion(); + } + else { + matchVersions(); + } + } + + /* Detect the Flash Player version for non-Internet Explorer browsers + - Detecting the plug-in version via the object element is more precise than using the plugins collection item's description: + a. Both release and build numbers can be detected + b. Avoid wrong descriptions by corrupt installers provided by Adobe + c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports + - Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available + */ + function testPlayerVersion() { + var b = doc.getElementsByTagName("body")[0]; + var o = createElement(OBJECT); + o.setAttribute("type", FLASH_MIME_TYPE); + var t = b.appendChild(o); + if (t) { + var counter = 0; + (function(){ + if (typeof t.GetVariable != UNDEF) { + var d = t.GetVariable("$version"); + if (d) { + d = d.split(" ")[1].split(","); + ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)]; + } + } + else if (counter < 10) { + counter++; + setTimeout(arguments.callee, 10); + return; + } + b.removeChild(o); + t = null; + matchVersions(); + })(); + } + else { + matchVersions(); + } + } + + /* Perform Flash Player and SWF version matching; static publishing only + */ + function matchVersions() { + var rl = regObjArr.length; + if (rl > 0) { + for (var i = 0; i < rl; i++) { // for each registered object element + var id = regObjArr[i].id; + var cb = regObjArr[i].callbackFn; + var cbObj = {success:false, id:id}; + if (ua.pv[0] > 0) { + var obj = getElementById(id); + if (obj) { + if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match! + setVisibility(id, true); + if (cb) { + cbObj.success = true; + cbObj.ref = getObjectById(id); + cb(cbObj); + } + } + else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported + var att = {}; + att.data = regObjArr[i].expressInstall; + att.width = obj.getAttribute("width") || "0"; + att.height = obj.getAttribute("height") || "0"; + if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); } + if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); } + // parse HTML object param element's name-value pairs + var par = {}; + var p = obj.getElementsByTagName("param"); + var pl = p.length; + for (var j = 0; j < pl; j++) { + if (p[j].getAttribute("name").toLowerCase() != "movie") { + par[p[j].getAttribute("name")] = p[j].getAttribute("value"); + } + } + showExpressInstall(att, par, id, cb); + } + else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF + displayAltContent(obj); + if (cb) { cb(cbObj); } + } + } + } + else { // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content) + setVisibility(id, true); + if (cb) { + var o = getObjectById(id); // test whether there is an HTML object element or not + if (o && typeof o.SetVariable != UNDEF) { + cbObj.success = true; + cbObj.ref = o; + } + cb(cbObj); + } + } + } + } + } + + function getObjectById(objectIdStr) { + var r = null; + var o = getElementById(objectIdStr); + if (o && o.nodeName == "OBJECT") { + if (typeof o.SetVariable != UNDEF) { + r = o; + } + else { + var n = o.getElementsByTagName(OBJECT)[0]; + if (n) { + r = n; + } + } + } + return r; + } + + /* Requirements for Adobe Express Install + - only one instance can be active at a time + - fp 6.0.65 or higher + - Win/Mac OS only + - no Webkit engines older than version 312 + */ + function canExpressInstall() { + return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312); + } + + /* Show the Adobe Express Install dialog + - Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75 + */ + function showExpressInstall(att, par, replaceElemIdStr, callbackFn) { + isExpressInstallActive = true; + storedCallbackFn = callbackFn || null; + storedCallbackObj = {success:false, id:replaceElemIdStr}; + var obj = getElementById(replaceElemIdStr); + if (obj) { + if (obj.nodeName == "OBJECT") { // static publishing + storedAltContent = abstractAltContent(obj); + storedAltContentId = null; + } + else { // dynamic publishing + storedAltContent = obj; + storedAltContentId = replaceElemIdStr; + } + att.id = EXPRESS_INSTALL_ID; + if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; } + if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; } + doc.title = doc.title.slice(0, 47) + " - Flash Player Installation"; + var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn", + fv = "MMredirectURL=" + encodeURI(window.location).toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title; + if (typeof par.flashvars != UNDEF) { + par.flashvars += "&" + fv; + } + else { + par.flashvars = fv; + } + // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it, + // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work + if (ua.ie && ua.win && obj.readyState != 4) { + var newObj = createElement("div"); + replaceElemIdStr += "SWFObjectNew"; + newObj.setAttribute("id", replaceElemIdStr); + obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf + obj.style.display = "none"; + (function(){ + if (obj.readyState == 4) { + obj.parentNode.removeChild(obj); + } + else { + setTimeout(arguments.callee, 10); + } + })(); + } + createSWF(att, par, replaceElemIdStr); + } + } + + /* Functions to abstract and display alternative content + */ + function displayAltContent(obj) { + if (ua.ie && ua.win && obj.readyState != 4) { + // IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it, + // because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work + var el = createElement("div"); + obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content + el.parentNode.replaceChild(abstractAltContent(obj), el); + obj.style.display = "none"; + (function(){ + if (obj.readyState == 4) { + obj.parentNode.removeChild(obj); + } + else { + setTimeout(arguments.callee, 10); + } + })(); + } + else { + obj.parentNode.replaceChild(abstractAltContent(obj), obj); + } + } + + function abstractAltContent(obj) { + var ac = createElement("div"); + if (ua.win && ua.ie) { + ac.innerHTML = obj.innerHTML; + } + else { + var nestedObj = obj.getElementsByTagName(OBJECT)[0]; + if (nestedObj) { + var c = nestedObj.childNodes; + if (c) { + var cl = c.length; + for (var i = 0; i < cl; i++) { + if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) { + ac.appendChild(c[i].cloneNode(true)); + } + } + } + } + } + return ac; + } + + /* Cross-browser dynamic SWF creation + */ + function createSWF(attObj, parObj, id) { + var r, el = getElementById(id); + if (ua.wk && ua.wk < 312) { return r; } + if (el) { + if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content + attObj.id = id; + } + if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML + var att = ""; + for (var i in attObj) { + if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries + if (i.toLowerCase() == "data") { + parObj.movie = attObj[i]; + } + else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword + att += ' class="' + attObj[i] + '"'; + } + else if (i.toLowerCase() != "classid") { + att += ' ' + i + '="' + attObj[i] + '"'; + } + } + } + var par = ""; + for (var j in parObj) { + if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries + par += ''; + } + } + el.outerHTML = '' + par + ''; + objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only) + r = getElementById(attObj.id); + } + else { // well-behaving browsers + var o = createElement(OBJECT); + o.setAttribute("type", FLASH_MIME_TYPE); + for (var m in attObj) { + if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries + if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword + o.setAttribute("class", attObj[m]); + } + else if (m.toLowerCase() != "classid") { // filter out IE specific attribute + o.setAttribute(m, attObj[m]); + } + } + } + for (var n in parObj) { + if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element + createObjParam(o, n, parObj[n]); + } + } + el.parentNode.replaceChild(o, el); + r = o; + } + } + return r; + } + + function createObjParam(el, pName, pValue) { + var p = createElement("param"); + p.setAttribute("name", pName); + p.setAttribute("value", pValue); + el.appendChild(p); + } + + /* Cross-browser SWF removal + - Especially needed to safely and completely remove a SWF in Internet Explorer + */ + function removeSWF(id) { + var obj = getElementById(id); + if (obj && obj.nodeName == "OBJECT") { + if (ua.ie && ua.win) { + obj.style.display = "none"; + (function(){ + if (obj.readyState == 4) { + removeObjectInIE(id); + } + else { + setTimeout(arguments.callee, 10); + } + })(); + } + else { + obj.parentNode.removeChild(obj); + } + } + } + + function removeObjectInIE(id) { + var obj = getElementById(id); + if (obj) { + for (var i in obj) { + if (typeof obj[i] == "function") { + obj[i] = null; + } + } + obj.parentNode.removeChild(obj); + } + } + + /* Functions to optimize JavaScript compression + */ + function getElementById(id) { + var el = null; + try { + el = doc.getElementById(id); + } + catch (e) {} + return el; + } + + function createElement(el) { + return doc.createElement(el); + } + + /* Updated attachEvent function for Internet Explorer + - Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks + */ + function addListener(target, eventType, fn) { + target.attachEvent(eventType, fn); + listenersArr[listenersArr.length] = [target, eventType, fn]; + } + + /* Flash Player and SWF content version matching + */ + function hasPlayerVersion(rv) { + var pv = ua.pv, v = rv.split("."); + v[0] = parseInt(v[0], 10); + v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0" + v[2] = parseInt(v[2], 10) || 0; + return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false; + } + + /* Cross-browser dynamic CSS creation + - Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php + */ + function createCSS(sel, decl, media, newStyle) { + if (ua.ie && ua.mac) { return; } + var h = doc.getElementsByTagName("head")[0]; + if (!h) { return; } // to also support badly authored HTML pages that lack a head element + var m = (media && typeof media == "string") ? media : "screen"; + if (newStyle) { + dynamicStylesheet = null; + dynamicStylesheetMedia = null; + } + if (!dynamicStylesheet || dynamicStylesheetMedia != m) { + // create dynamic stylesheet + get a global reference to it + var s = createElement("style"); + s.setAttribute("type", "text/css"); + s.setAttribute("media", m); + dynamicStylesheet = h.appendChild(s); + if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) { + dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1]; + } + dynamicStylesheetMedia = m; + } + // add style rule + if (ua.ie && ua.win) { + if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) { + dynamicStylesheet.addRule(sel, decl); + } + } + else { + if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) { + dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}")); + } + } + } + + function setVisibility(id, isVisible) { + if (!autoHideShow) { return; } + var v = isVisible ? "visible" : "hidden"; + if (isDomLoaded && getElementById(id)) { + getElementById(id).style.visibility = v; + } + else { + createCSS("#" + id, "visibility:" + v); + } + } + + /* Filter to avoid XSS attacks + */ + function urlEncodeIfNecessary(s) { + var regex = /[\\\"<>\.;]/; + var hasBadChars = regex.exec(s) != null; + return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s; + } + + /* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only) + */ + var cleanup = function() { + if (ua.ie && ua.win) { + window.attachEvent("onunload", function() { + // remove listeners to avoid memory leaks + var ll = listenersArr.length; + for (var i = 0; i < ll; i++) { + listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]); + } + // cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect + var il = objIdArr.length; + for (var j = 0; j < il; j++) { + removeSWF(objIdArr[j]); + } + // cleanup library's main closures to avoid memory leaks + for (var k in ua) { + ua[k] = null; + } + ua = null; + for (var l in swfobject) { + swfobject[l] = null; + } + swfobject = null; + }); + } + }(); + + return { + /* Public API + - Reference: http://code.google.com/p/swfobject/wiki/documentation + */ + registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) { + if (ua.w3 && objectIdStr && swfVersionStr) { + var regObj = {}; + regObj.id = objectIdStr; + regObj.swfVersion = swfVersionStr; + regObj.expressInstall = xiSwfUrlStr; + regObj.callbackFn = callbackFn; + regObjArr[regObjArr.length] = regObj; + setVisibility(objectIdStr, false); + } + else if (callbackFn) { + callbackFn({success:false, id:objectIdStr}); + } + }, + + getObjectById: function(objectIdStr) { + if (ua.w3) { + return getObjectById(objectIdStr); + } + }, + + embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) { + var callbackObj = {success:false, id:replaceElemIdStr}; + if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) { + setVisibility(replaceElemIdStr, false); + addDomLoadEvent(function() { + widthStr += ""; // auto-convert to string + heightStr += ""; + var att = {}; + if (attObj && typeof attObj === OBJECT) { + for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs + att[i] = attObj[i]; + } + } + att.data = swfUrlStr; + att.width = widthStr; + att.height = heightStr; + var par = {}; + if (parObj && typeof parObj === OBJECT) { + for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs + par[j] = parObj[j]; + } + } + if (flashvarsObj && typeof flashvarsObj === OBJECT) { + for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs + if (typeof par.flashvars != UNDEF) { + par.flashvars += "&" + k + "=" + flashvarsObj[k]; + } + else { + par.flashvars = k + "=" + flashvarsObj[k]; + } + } + } + if (hasPlayerVersion(swfVersionStr)) { // create SWF + var obj = createSWF(att, par, replaceElemIdStr); + if (att.id == replaceElemIdStr) { + setVisibility(replaceElemIdStr, true); + } + callbackObj.success = true; + callbackObj.ref = obj; + } + else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install + att.data = xiSwfUrlStr; + showExpressInstall(att, par, replaceElemIdStr, callbackFn); + return; + } + else { // show alternative content + setVisibility(replaceElemIdStr, true); + } + if (callbackFn) { callbackFn(callbackObj); } + }); + } + else if (callbackFn) { callbackFn(callbackObj); } + }, + + switchOffAutoHideShow: function() { + autoHideShow = false; + }, + + ua: ua, + + getFlashPlayerVersion: function() { + return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] }; + }, + + hasFlashPlayerVersion: hasPlayerVersion, + + createSWF: function(attObj, parObj, replaceElemIdStr) { + if (ua.w3) { + return createSWF(attObj, parObj, replaceElemIdStr); + } + else { + return undefined; + } + }, + + showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) { + if (ua.w3 && canExpressInstall()) { + showExpressInstall(att, par, replaceElemIdStr, callbackFn); + } + }, + + removeSWF: function(objElemIdStr) { + if (ua.w3) { + removeSWF(objElemIdStr); + } + }, + + createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) { + if (ua.w3) { + createCSS(selStr, declStr, mediaStr, newStyleBoolean); + } + }, + + addDomLoadEvent: addDomLoadEvent, + + addLoadEvent: addLoadEvent, + + getQueryParamValue: function(param) { + var q = doc.location.search || doc.location.hash; + if (q) { + if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark + if (param == null) { + return urlEncodeIfNecessary(q); + } + var pairs = q.split("&"); + for (var i = 0; i < pairs.length; i++) { + if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) { + return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1))); + } + } + } + return ""; + }, + + // For internal usage only + expressInstallCallback: function() { + if (isExpressInstallActive) { + var obj = getElementById(EXPRESS_INSTALL_ID); + if (obj && storedAltContent) { + obj.parentNode.replaceChild(storedAltContent, obj); + if (storedAltContentId) { + setVisibility(storedAltContentId, true); + if (ua.ie && ua.win) { storedAltContent.style.display = "block"; } + } + if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); } + } + isExpressInstallActive = false; + } + } + }; +}(); diff --git a/spine-starling/spine-starling-example/src/Game.as b/spine-starling/spine-starling-example/src/Game.as new file mode 100644 index 000000000..bf5a1ebca --- /dev/null +++ b/spine-starling/spine-starling-example/src/Game.as @@ -0,0 +1,38 @@ +package { + +import spine.SkeletonAnimationSprite; +import spine.SkeletonData; +import spine.StarlingSkeletonJson; + +import starling.display.Sprite; +import starling.textures.Texture; +import starling.textures.TextureAtlas; + +public class Game extends Sprite { + [Embed(source = "spineboy.xml", mimeType = "application/octet-stream")] + static public const SpineboyAtlasXml:Class; + + [Embed(source = "spineboy.png")] + static public const SpineboyAtlasTexture:Class; + + [Embed(source = "spineboy.json", mimeType = "application/octet-stream")] + static public const SpineboyJson:Class; + + public function Game () { + var texture:Texture = Texture.fromBitmap(new SpineboyAtlasTexture()); + var xml:XML = XML(new SpineboyAtlasXml()); + var atlas:TextureAtlas = new TextureAtlas(texture, xml); + + var json:StarlingSkeletonJson = new StarlingSkeletonJson(atlas); + var skeletonData:SkeletonData = json.readSkeletonData(new SpineboyJson()); + + var skeleton:SkeletonAnimationSprite = new SkeletonAnimationSprite(skeletonData); + skeleton.x = 320; + skeleton.y = 420; + skeleton.width = 100; + skeleton.height = 100; + skeleton.setAnimation("walk", true); + addChild(skeleton); + } +} +} diff --git a/spine-starling/spine-starling-example/src/Main.as b/spine-starling/spine-starling-example/src/Main.as new file mode 100644 index 000000000..ab6b7c99d --- /dev/null +++ b/spine-starling/spine-starling-example/src/Main.as @@ -0,0 +1,17 @@ + +package { + +import flash.display.Sprite; +import starling.core.Starling; + +[SWF(width = "640", height = "480", frameRate = "60", backgroundColor = "#dddddd")] +public class Main extends Sprite { + private var _starling:Starling; + + public function Main () { + _starling = new Starling(Game, stage); + _starling.start(); + } +} + +} diff --git a/spine-starling/spine-starling-example/src/spineboy.json b/spine-starling/spine-starling-example/src/spineboy.json new file mode 100644 index 000000000..57b0f6cbe --- /dev/null +++ b/spine-starling/spine-starling-example/src/spineboy.json @@ -0,0 +1,787 @@ +{ +"bones": [ + { "name": "root" }, + { "name": "hip", "parent": "root", "x": 0.64, "y": 114.41 }, + { "name": "left upper leg", "parent": "hip", "length": 50.39, "x": 14.45, "y": 2.81, "rotation": -89.09 }, + { "name": "left lower leg", "parent": "left upper leg", "length": 56.45, "x": 51.78, "y": 3.46, "rotation": -16.65 }, + { "name": "left foot", "parent": "left lower leg", "length": 46.5, "x": 64.02, "y": -8.67, "rotation": 102.43 }, + { "name": "right upper leg", "parent": "hip", "length": 45.76, "x": -18.27, "rotation": -101.13 }, + { "name": "right lower leg", "parent": "right upper leg", "length": 58.52, "x": 50.21, "y": 0.6, "rotation": -10.7 }, + { "name": "right foot", "parent": "right lower leg", "length": 45.45, "x": 64.88, "y": 0.04, "rotation": 110.3 }, + { "name": "torso", "parent": "hip", "length": 85.82, "x": -6.42, "y": 1.97, "rotation": 94.95 }, + { "name": "neck", "parent": "torso", "length": 18.38, "x": 83.64, "y": -1.78, "rotation": 0.9 }, + { "name": "head", "parent": "neck", "length": 68.28, "x": 19.09, "y": 6.97, "rotation": -8.94 }, + { "name": "right shoulder", "parent": "torso", "length": 49.95, "x": 81.9, "y": 6.79, "rotation": 130.6 }, + { "name": "right arm", "parent": "right shoulder", "length": 36.74, "x": 49.95, "y": -0.12, "rotation": 40.12 }, + { "name": "right hand", "parent": "right arm", "length": 15.32, "x": 36.9, "y": 0.34, "rotation": 2.35 }, + { "name": "left shoulder", "parent": "torso", "length": 44.19, "x": 78.96, "y": -15.75, "rotation": -156.96 }, + { "name": "left arm", "parent": "left shoulder", "length": 35.62, "x": 44.19, "y": -0.01, "rotation": 28.16 }, + { "name": "left hand", "parent": "left arm", "length": 11.52, "x": 35.62, "y": 0.07, "rotation": 2.7 }, + { "name": "pelvis", "parent": "hip", "x": 1.41, "y": -6.57 } +], +"slots": [ + { "name": "left shoulder", "bone": "left shoulder", "attachment": "left-shoulder" }, + { "name": "left arm", "bone": "left arm", "attachment": "left-arm" }, + { "name": "left hand", "bone": "left hand", "attachment": "left-hand" }, + { "name": "left foot", "bone": "left foot", "attachment": "left-foot" }, + { "name": "left lower leg", "bone": "left lower leg", "attachment": "left-lower-leg" }, + { "name": "left upper leg", "bone": "left upper leg", "attachment": "left-upper-leg" }, + { "name": "pelvis", "bone": "pelvis", "attachment": "pelvis" }, + { "name": "right foot", "bone": "right foot", "attachment": "right-foot" }, + { "name": "right lower leg", "bone": "right lower leg", "attachment": "right-lower-leg" }, + { "name": "right upper leg", "bone": "right upper leg", "attachment": "right-upper-leg" }, + { "name": "torso", "bone": "torso", "attachment": "torso" }, + { "name": "neck", "bone": "neck", "attachment": "neck" }, + { "name": "head", "bone": "head", "attachment": "head" }, + { "name": "eyes", "bone": "head", "attachment": "eyes" }, + { "name": "right shoulder", "bone": "right shoulder", "attachment": "right-shoulder" }, + { "name": "right arm", "bone": "right arm", "attachment": "right-arm" }, + { "name": "right hand", "bone": "right hand", "attachment": "right-hand" } +], +"skins": { + "default": { + "left shoulder": { + "left-shoulder": { "x": 23.74, "y": 0.11, "rotation": 62.01, "width": 34, "height": 53 } + }, + "left arm": { + "left-arm": { "x": 15.11, "y": -0.44, "rotation": 33.84, "width": 35, "height": 29 } + }, + "left hand": { + "left-hand": { "x": 0.75, "y": 1.86, "rotation": 31.14, "width": 35, "height": 38 } + }, + "left foot": { + "left-foot": { "x": 24.35, "y": 8.88, "rotation": 3.32, "width": 65, "height": 30 } + }, + "left lower leg": { + "left-lower-leg": { "x": 24.55, "y": -1.92, "rotation": 105.75, "width": 49, "height": 64 } + }, + "left upper leg": { + "left-upper-leg": { "x": 26.12, "y": -1.85, "rotation": 89.09, "width": 33, "height": 67 } + }, + "pelvis": { + "pelvis": { "x": -4.83, "y": 10.62, "width": 63, "height": 47 } + }, + "right foot": { + "right-foot": { "x": 19.02, "y": 8.47, "rotation": 1.52, "width": 67, "height": 30 } + }, + "right lower leg": { + "right-lower-leg": { "x": 23.28, "y": -2.59, "rotation": 111.83, "width": 51, "height": 64 } + }, + "right upper leg": { + "right-upper-leg": { "x": 23.03, "y": 0.25, "rotation": 101.13, "width": 44, "height": 70 } + }, + "torso": { + "torso": { "x": 44.57, "y": -7.08, "rotation": -94.95, "width": 68, "height": 92 } + }, + "neck": { + "neck": { "x": 9.42, "y": -3.66, "rotation": -100.15, "width": 34, "height": 28 } + }, + "head": { + "head": { "x": 53.94, "y": -5.75, "rotation": -86.9, "width": 121, "height": 132 } + }, + "eyes": { + "eyes": { "x": 28.94, "y": -32.92, "rotation": -86.9, "width": 34, "height": 27 }, + "eyes-closed": { "x": 28.77, "y": -32.86, "rotation": -86.9, "width": 34, "height": 27 } + }, + "right shoulder": { + "right-shoulder": { "x": 25.86, "y": 0.03, "rotation": 134.44, "width": 52, "height": 51 } + }, + "right arm": { + "right-arm": { "x": 18.34, "y": -2.64, "rotation": 94.32, "width": 21, "height": 45 } + }, + "right hand": { + "right-hand": { "x": 6.82, "y": 1.25, "rotation": 91.96, "width": 32, "height": 32 } + } + } +}, +"animations": { + "walk": { + "bones": { + "left upper leg": { + "rotate": [ + { "time": 0, "angle": -26.55 }, + { "time": 0.1333, "angle": -8.78 }, + { "time": 0.2666, "angle": 9.51 }, + { "time": 0.4, "angle": 30.74 }, + { "time": 0.5333, "angle": 25.33 }, + { "time": 0.6666, "angle": 26.11 }, + { "time": 0.8, "angle": -7.7 }, + { "time": 0.9333, "angle": -21.19 }, + { "time": 1.0666, "angle": -26.55 } + ], + "translate": [ + { "time": 0, "x": -3, "y": -2.25 }, + { "time": 0.4, "x": -2.18, "y": -2.25 }, + { "time": 1.0666, "x": -3, "y": -2.25 } + ] + }, + "right upper leg": { + "rotate": [ + { "time": 0, "angle": 42.45 }, + { "time": 0.1333, "angle": 52.1 }, + { "time": 0.2666, "angle": 5.96 }, + { "time": 0.5333, "angle": -16.93 }, + { "time": 0.6666, "angle": 1.89 }, + { + "time": 0.8, + "angle": 28.06, + "curve": [ 0.462, 0.11, 1, 1 ] + }, + { + "time": 0.9333, + "angle": 58.68, + "curve": [ 0.5, 0.02, 1, 1 ] + }, + { "time": 1.0666, "angle": 42.45 } + ], + "translate": [ + { "time": 0, "x": 8.11, "y": -2.36 }, + { "time": 0.1333, "x": 10.03, "y": -2.56 }, + { "time": 0.4, "x": 2.76, "y": -2.97 }, + { "time": 0.5333, "x": 2.76, "y": -2.81 }, + { "time": 0.9333, "x": 8.67, "y": -2.54 }, + { "time": 1.0666, "x": 8.11, "y": -2.36 } + ] + }, + "left lower leg": { + "rotate": [ + { "time": 0, "angle": -10.21 }, + { "time": 0.1333, "angle": -55.64 }, + { "time": 0.2666, "angle": -68.12 }, + { "time": 0.5333, "angle": 5.11 }, + { "time": 0.6666, "angle": -28.29 }, + { "time": 0.8, "angle": 4.08 }, + { "time": 0.9333, "angle": 3.53 }, + { "time": 1.0666, "angle": -10.21 } + ] + }, + "left foot": { + "rotate": [ + { "time": 0, "angle": -3.69 }, + { "time": 0.1333, "angle": -10.42 }, + { "time": 0.2666, "angle": -17.14 }, + { "time": 0.4, "angle": -2.83 }, + { "time": 0.5333, "angle": -3.87 }, + { "time": 0.6666, "angle": 2.78 }, + { "time": 0.8, "angle": 1.68 }, + { "time": 0.9333, "angle": -8.54 }, + { "time": 1.0666, "angle": -3.69 } + ] + }, + "right shoulder": { + "rotate": [ + { + "time": 0, + "angle": 20.89, + "curve": [ 0.264, 0, 0.75, 1 ] + }, + { + "time": 0.1333, + "angle": 3.72, + "curve": [ 0.272, 0, 0.841, 1 ] + }, + { "time": 0.6666, "angle": -278.28 }, + { "time": 1.0666, "angle": 20.89 } + ], + "translate": [ + { "time": 0, "x": -7.84, "y": 7.19 }, + { "time": 0.1333, "x": -6.36, "y": 6.42 }, + { "time": 0.6666, "x": -11.07, "y": 5.25 }, + { "time": 1.0666, "x": -7.84, "y": 7.19 } + ] + }, + "right arm": { + "rotate": [ + { + "time": 0, + "angle": -4.02, + "curve": [ 0.267, 0, 0.804, 0.99 ] + }, + { + "time": 0.1333, + "angle": -13.99, + "curve": [ 0.341, 0, 1, 1 ] + }, + { + "time": 0.6666, + "angle": 36.54, + "curve": [ 0.307, 0, 0.787, 0.99 ] + }, + { "time": 1.0666, "angle": -4.02 } + ] + }, + "right hand": { + "rotate": [ + { "time": 0, "angle": 22.92 }, + { "time": 0.4, "angle": -8.97 }, + { "time": 0.6666, "angle": 0.51 }, + { "time": 1.0666, "angle": 22.92 } + ] + }, + "left shoulder": { + "rotate": [ + { "time": 0, "angle": -1.47 }, + { "time": 0.1333, "angle": 13.6 }, + { "time": 0.6666, "angle": 280.74 }, + { "time": 1.0666, "angle": -1.47 } + ], + "translate": [ + { "time": 0, "x": -1.76, "y": 0.56 }, + { "time": 0.6666, "x": -2.47, "y": 8.14 }, + { "time": 1.0666, "x": -1.76, "y": 0.56 } + ] + }, + "left hand": { + "rotate": [ + { + "time": 0, + "angle": 11.58, + "curve": [ 0.169, 0.37, 0.632, 1.55 ] + }, + { + "time": 0.1333, + "angle": 28.13, + "curve": [ 0.692, 0, 0.692, 0.99 ] + }, + { + "time": 0.6666, + "angle": -27.42, + "curve": [ 0.117, 0.41, 0.738, 1.76 ] + }, + { "time": 0.8, "angle": -36.32 }, + { "time": 1.0666, "angle": 11.58 } + ] + }, + "left arm": { + "rotate": [ + { "time": 0, "angle": -8.27 }, + { "time": 0.1333, "angle": 18.43 }, + { "time": 0.6666, "angle": 0.88 }, + { "time": 1.0666, "angle": -8.27 } + ] + }, + "torso": { + "rotate": [ + { "time": 0, "angle": -10.28 }, + { + "time": 0.1333, + "angle": -15.38, + "curve": [ 0.545, 0, 1, 1 ] + }, + { + "time": 0.4, + "angle": -9.78, + "curve": [ 0.58, 0.17, 1, 1 ] + }, + { "time": 0.6666, "angle": -15.75 }, + { "time": 0.9333, "angle": -7.06 }, + { "time": 1.0666, "angle": -10.28 } + ], + "translate": [ + { "time": 0, "x": -3.67, "y": 1.68 }, + { "time": 0.1333, "x": -3.67, "y": 0.68 }, + { "time": 0.4, "x": -3.67, "y": 1.97 }, + { "time": 0.6666, "x": -3.67, "y": -0.14 }, + { "time": 1.0666, "x": -3.67, "y": 1.68 } + ] + }, + "right foot": { + "rotate": [ + { "time": 0, "angle": -5.25 }, + { "time": 0.2666, "angle": -4.08 }, + { "time": 0.4, "angle": -6.45 }, + { "time": 0.5333, "angle": -5.39 }, + { "time": 0.8, "angle": -11.68 }, + { "time": 0.9333, "angle": 0.46 }, + { "time": 1.0666, "angle": -5.25 } + ] + }, + "right lower leg": { + "rotate": [ + { "time": 0, "angle": -3.39 }, + { "time": 0.1333, "angle": -45.53 }, + { "time": 0.2666, "angle": -2.59 }, + { "time": 0.5333, "angle": -19.53 }, + { "time": 0.6666, "angle": -64.8 }, + { + "time": 0.8, + "angle": -82.56, + "curve": [ 0.557, 0.18, 1, 1 ] + }, + { "time": 1.0666, "angle": -3.39 } + ] + }, + "hip": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 1.0666, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0 }, + { + "time": 0.1333, + "x": 0, + "y": -7.61, + "curve": [ 0.272, 0.86, 1, 1 ] + }, + { "time": 0.4, "x": 0, "y": 8.7 }, + { "time": 0.5333, "x": 0, "y": -0.41 }, + { + "time": 0.6666, + "x": 0, + "y": -7.05, + "curve": [ 0.235, 0.89, 1, 1 ] + }, + { "time": 0.8, "x": 0, "y": 2.92 }, + { "time": 0.9333, "x": 0, "y": 6.78 }, + { "time": 1.0666, "x": 0, "y": 0 } + ] + }, + "neck": { + "rotate": [ + { "time": 0, "angle": 3.6 }, + { "time": 0.1333, "angle": 17.49 }, + { "time": 0.2666, "angle": 6.1 }, + { "time": 0.4, "angle": 3.45 }, + { "time": 0.5333, "angle": 5.17 }, + { "time": 0.6666, "angle": 18.36 }, + { "time": 0.8, "angle": 6.09 }, + { "time": 0.9333, "angle": 2.28 }, + { "time": 1.0666, "angle": 3.6 } + ] + }, + "head": { + "rotate": [ + { + "time": 0, + "angle": 3.6, + "curve": [ 0, 0, 0.704, 1.61 ] + }, + { "time": 0.1666, "angle": -0.2 }, + { "time": 0.2666, "angle": 6.1 }, + { "time": 0.4, "angle": 3.45 }, + { + "time": 0.5333, + "angle": 5.17, + "curve": [ 0, 0, 0.704, 1.61 ] + }, + { "time": 0.7, "angle": 1.1 }, + { "time": 0.8, "angle": 6.09 }, + { "time": 0.9333, "angle": 2.28 }, + { "time": 1.0666, "angle": 3.6 } + ] + } + } + }, + "jump": { + "bones": { + "hip": { + "rotate": [ + { "time": 0, "angle": 0, "curve": "stepped" }, + { "time": 0.9333, "angle": 0, "curve": "stepped" }, + { "time": 1.3666, "angle": 0 } + ], + "translate": [ + { "time": 0, "x": -11.57, "y": -3 }, + { "time": 0.2333, "x": -16.2, "y": -19.43 }, + { + "time": 0.3333, + "x": 7.66, + "y": -8.48, + "curve": [ 0.057, 0.06, 0.712, 1 ] + }, + { "time": 0.3666, "x": 15.38, "y": 5.01 }, + { "time": 0.4666, "x": -7.84, "y": 57.22 }, + { + "time": 0.6, + "x": -10.81, + "y": 96.34, + "curve": [ 0.241, 0, 1, 1 ] + }, + { "time": 0.7333, "x": -7.01, "y": 54.7 }, + { "time": 0.8, "x": -10.58, "y": 32.2 }, + { "time": 0.9333, "x": -31.99, "y": 0.45 }, + { "time": 1.0666, "x": -12.48, "y": -29.47 }, + { "time": 1.3666, "x": -11.57, "y": -3 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "left upper leg": { + "rotate": [ + { "time": 0, "angle": 17.13 }, + { "time": 0.2333, "angle": 44.35 }, + { "time": 0.3333, "angle": 16.46 }, + { "time": 0.4, "angle": -9.88 }, + { "time": 0.4666, "angle": -11.42 }, + { "time": 0.5666, "angle": 23.46 }, + { "time": 0.7666, "angle": 71.82 }, + { "time": 0.9333, "angle": 65.53 }, + { "time": 1.0666, "angle": 51.01 }, + { "time": 1.3666, "angle": 17.13 } + ], + "translate": [ + { "time": 0, "x": -3, "y": -2.25, "curve": "stepped" }, + { "time": 0.9333, "x": -3, "y": -2.25, "curve": "stepped" }, + { "time": 1.3666, "x": -3, "y": -2.25 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "left lower leg": { + "rotate": [ + { "time": 0, "angle": -16.25 }, + { "time": 0.2333, "angle": -52.21 }, + { "time": 0.4, "angle": 15.04 }, + { "time": 0.4666, "angle": -8.95 }, + { "time": 0.5666, "angle": -39.53 }, + { "time": 0.7666, "angle": -27.27 }, + { "time": 0.9333, "angle": -3.52 }, + { "time": 1.0666, "angle": -61.92 }, + { "time": 1.3666, "angle": -16.25 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 1.3666, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "left foot": { + "rotate": [ + { "time": 0, "angle": 0.33 }, + { "time": 0.2333, "angle": 6.2 }, + { "time": 0.3333, "angle": 14.73 }, + { "time": 0.4, "angle": -15.54 }, + { "time": 0.4333, "angle": -21.2 }, + { "time": 0.5666, "angle": -7.55 }, + { "time": 0.7666, "angle": -0.67 }, + { "time": 0.9333, "angle": -0.58 }, + { "time": 1.0666, "angle": 14.64 }, + { "time": 1.3666, "angle": 0.33 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 1.3666, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "right upper leg": { + "rotate": [ + { "time": 0, "angle": 25.97 }, + { "time": 0.2333, "angle": 46.43 }, + { "time": 0.3333, "angle": 22.61 }, + { "time": 0.4, "angle": 2.13 }, + { + "time": 0.4666, + "angle": 0.04, + "curve": [ 0, 0, 0.637, 0.98 ] + }, + { "time": 0.6, "angle": 65.55 }, + { "time": 0.7666, "angle": 64.93 }, + { "time": 0.9333, "angle": 41.08 }, + { "time": 1.0666, "angle": 66.25 }, + { "time": 1.3666, "angle": 25.97 } + ], + "translate": [ + { "time": 0, "x": 5.74, "y": 0.61 }, + { "time": 0.2333, "x": 4.79, "y": 1.79 }, + { "time": 0.3333, "x": 6.05, "y": -4.55 }, + { "time": 0.9333, "x": 4.79, "y": 1.79, "curve": "stepped" }, + { "time": 1.0666, "x": 4.79, "y": 1.79 }, + { "time": 1.3666, "x": 5.74, "y": 0.61 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "right lower leg": { + "rotate": [ + { "time": 0, "angle": -27.46 }, + { "time": 0.2333, "angle": -64.03 }, + { "time": 0.4, "angle": -48.36 }, + { "time": 0.5666, "angle": -76.86 }, + { "time": 0.7666, "angle": -26.89 }, + { "time": 0.9, "angle": -18.97 }, + { "time": 0.9333, "angle": -14.18 }, + { "time": 1.0666, "angle": -80.45 }, + { "time": 1.3666, "angle": -27.46 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 1.3666, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "right foot": { + "rotate": [ + { "time": 0, "angle": 1.08 }, + { "time": 0.2333, "angle": 16.02 }, + { "time": 0.3, "angle": 12.94 }, + { "time": 0.3333, "angle": 15.16 }, + { "time": 0.4, "angle": -14.7 }, + { "time": 0.4333, "angle": -12.85 }, + { "time": 0.4666, "angle": -19.18 }, + { "time": 0.5666, "angle": -15.82 }, + { "time": 0.6, "angle": -3.59 }, + { "time": 0.7666, "angle": -3.56 }, + { "time": 0.9333, "angle": 1.86 }, + { "time": 1.0666, "angle": 16.02 }, + { "time": 1.3666, "angle": 1.08 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 1.3666, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "torso": { + "rotate": [ + { "time": 0, "angle": -13.35 }, + { "time": 0.2333, "angle": -48.95 }, + { "time": 0.4333, "angle": -35.77 }, + { "time": 0.6, "angle": -4.59 }, + { "time": 0.7666, "angle": 14.61 }, + { "time": 0.9333, "angle": 15.74 }, + { "time": 1.0666, "angle": -32.44 }, + { "time": 1.3666, "angle": -13.35 } + ], + "translate": [ + { "time": 0, "x": -3.67, "y": 1.68, "curve": "stepped" }, + { "time": 0.9333, "x": -3.67, "y": 1.68, "curve": "stepped" }, + { "time": 1.3666, "x": -3.67, "y": 1.68 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "neck": { + "rotate": [ + { "time": 0, "angle": 12.78 }, + { "time": 0.2333, "angle": 16.46 }, + { "time": 0.4, "angle": 26.49 }, + { "time": 0.6, "angle": 15.51 }, + { "time": 0.7666, "angle": 1.34 }, + { "time": 0.9333, "angle": 2.35 }, + { "time": 1.0666, "angle": 6.08 }, + { "time": 1.3, "angle": 21.23 }, + { "time": 1.3666, "angle": 12.78 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 1.3666, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "head": { + "rotate": [ + { "time": 0, "angle": 5.19 }, + { "time": 0.2333, "angle": 20.27 }, + { "time": 0.4, "angle": 15.27 }, + { "time": 0.6, "angle": -24.69 }, + { "time": 0.7666, "angle": -11.02 }, + { "time": 0.9333, "angle": -24.38 }, + { "time": 1.0666, "angle": 11.99 }, + { "time": 1.3, "angle": 4.86 }, + { "time": 1.3666, "angle": 5.19 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 1.3666, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "left shoulder": { + "rotate": [ + { + "time": 0, + "angle": 0.05, + "curve": [ 0, 0, 0.62, 1 ] + }, + { + "time": 0.2333, + "angle": 279.66, + "curve": [ 0.218, 0.67, 0.66, 0.99 ] + }, + { + "time": 0.5, + "angle": 62.27, + "curve": [ 0.462, 0, 0.764, 0.58 ] + }, + { "time": 0.9333, "angle": 28.91 }, + { "time": 1.0666, "angle": -8.62 }, + { "time": 1.1666, "angle": -18.43 }, + { "time": 1.3666, "angle": 0.05 } + ], + "translate": [ + { "time": 0, "x": -1.76, "y": 0.56, "curve": "stepped" }, + { "time": 0.9333, "x": -1.76, "y": 0.56, "curve": "stepped" }, + { "time": 1.3666, "x": -1.76, "y": 0.56 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "left hand": { + "rotate": [ + { "time": 0, "angle": 11.58, "curve": "stepped" }, + { "time": 0.9333, "angle": 11.58, "curve": "stepped" }, + { "time": 1.3666, "angle": 11.58 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 1.3666, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "left arm": { + "rotate": [ + { "time": 0, "angle": 0.51 }, + { "time": 0.4333, "angle": 12.82 }, + { "time": 0.6, "angle": 47.55 }, + { "time": 0.9333, "angle": 12.82 }, + { "time": 1.1666, "angle": -6.5 }, + { "time": 1.3666, "angle": 0.51 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 1.3666, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "right shoulder": { + "rotate": [ + { + "time": 0, + "angle": 43.82, + "curve": [ 0, 0, 0.62, 1 ] + }, + { + "time": 0.2333, + "angle": -8.74, + "curve": [ 0.304, 0.58, 0.709, 0.97 ] + }, + { + "time": 0.5333, + "angle": -208.02, + "curve": [ 0.462, 0, 0.764, 0.58 ] + }, + { "time": 0.9333, "angle": -246.72 }, + { "time": 1.0666, "angle": -307.13 }, + { "time": 1.1666, "angle": 37.15 }, + { "time": 1.3666, "angle": 43.82 } + ], + "translate": [ + { "time": 0, "x": -7.84, "y": 7.19, "curve": "stepped" }, + { "time": 0.9333, "x": -7.84, "y": 7.19, "curve": "stepped" }, + { "time": 1.3666, "x": -7.84, "y": 7.19 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "right arm": { + "rotate": [ + { "time": 0, "angle": -4.02 }, + { "time": 0.6, "angle": 17.5 }, + { "time": 0.9333, "angle": -4.02 }, + { "time": 1.1666, "angle": -16.72 }, + { "time": 1.3666, "angle": -4.02 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 1.3666, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "right hand": { + "rotate": [ + { "time": 0, "angle": 22.92, "curve": "stepped" }, + { "time": 0.9333, "angle": 22.92, "curve": "stepped" }, + { "time": 1.3666, "angle": 22.92 } + ], + "translate": [ + { "time": 0, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 0.9333, "x": 0, "y": 0, "curve": "stepped" }, + { "time": 1.3666, "x": 0, "y": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 0.9333, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + }, + "root": { + "rotate": [ + { "time": 0, "angle": 0 }, + { "time": 0.4333, "angle": -14.52 }, + { "time": 0.8, "angle": 9.86 }, + { "time": 1.3666, "angle": 0 } + ], + "scale": [ + { "time": 0, "x": 1, "y": 1, "curve": "stepped" }, + { "time": 1.3666, "x": 1, "y": 1 } + ] + } + } + } +} +} \ No newline at end of file diff --git a/spine-starling/spine-starling-example/src/spineboy.png b/spine-starling/spine-starling-example/src/spineboy.png new file mode 100644 index 000000000..a34d38af0 Binary files /dev/null and b/spine-starling/spine-starling-example/src/spineboy.png differ diff --git a/spine-starling/spine-starling-example/src/spineboy.xml b/spine-starling/spine-starling-example/src/spineboy.xml new file mode 100644 index 000000000..ce9a1ae12 --- /dev/null +++ b/spine-starling/spine-starling-example/src/spineboy.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spine-starling/spine-starling/.actionScriptProperties b/spine-starling/spine-starling/.actionScriptProperties new file mode 100644 index 000000000..6d664a1a1 --- /dev/null +++ b/spine-starling/spine-starling/.actionScriptProperties @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spine-starling/spine-starling/.flexLibProperties b/spine-starling/spine-starling/.flexLibProperties new file mode 100644 index 000000000..1fbacb45b --- /dev/null +++ b/spine-starling/spine-starling/.flexLibProperties @@ -0,0 +1,6 @@ + + + + + + diff --git a/spine-starling/spine-starling/.project b/spine-starling/spine-starling/.project new file mode 100644 index 000000000..c658a3a4e --- /dev/null +++ b/spine-starling/spine-starling/.project @@ -0,0 +1,18 @@ + + + spine-starling + + + + + + com.adobe.flexbuilder.project.flexbuilder + + + + + + com.adobe.flexbuilder.project.aslibnature + com.adobe.flexbuilder.project.actionscriptnature + + diff --git a/spine-starling/spine-starling/.settings/FlexPrettyPrintCommand.prefs b/spine-starling/spine-starling/.settings/FlexPrettyPrintCommand.prefs new file mode 100644 index 000000000..5ef23d336 --- /dev/null +++ b/spine-starling/spine-starling/.settings/FlexPrettyPrintCommand.prefs @@ -0,0 +1,202 @@ +#Tue Apr 30 18:56:15 CEST 2013 +ASRearr_AddDefaultHeaderForProperties=1 +ASRearr_AddDefaultHeaderForStaticProperties=1 +ASRearr_BlankLinesBeforeElement= +ASRearr_CopyrightHeader=style\=1|width\=80|blankLines\=2|fillChar\=-|text\= Copyright 2013\n All rights reserved.|linesBefore\=1| +ASRearr_ElementFunctionVisibilityOrder=public\:true,protected\:true,internal\:true,private\:true, +ASRearr_ElementOrder=Import,Include,Namespace Definition,Default Namespace,Namespace Use,Static Property,Static Function,Static Initializer,Constructor,Property,Function, +ASRearr_ElementPropertyVisibilityOrder=public\:true,protected\:true,internal\:true,private\:true, +ASRearr_ElementSortFunctions=true +ASRearr_ElementSortImports=true +ASRearr_ElementSortIncludes=true +ASRearr_ElementSortMetatags=false +ASRearr_ElementSortNamespaces=true +ASRearr_ElementSortProperties=true +ASRearr_ElementSortStaticFunctions=true +ASRearr_ElementSortStaticProperties=true +ASRearr_ElementStaticFunctionVisibilityOrder=public\:true,protected\:true,internal\:true,private\:true, +ASRearr_ElementStaticPropertyVisibilityOrder=public\:true,protected\:true,internal\:true,private\:true, +ASRearr_GroupGettersAndSettersWithProperties=false +ASRearr_GroupGettersAndSettersWithStaticProperties=false +ASRearr_ImportOrder=adobe,com,flash,mx, +ASRearr_MajorSectionHeader=style\=1|width\=60|blankLines\=4|fillChar\=-|text\=|linesBefore\=1| +ASRearr_MetatagOrder=ArrayElementType,Bindable,DefaultProperty,Deprecated,Effect,Embed,Event,Exclude,ExcludeClass,IconFile,Inspectable,InstanceType,NonCommittingChangeEvent,RemoteClass,Style,SWF,Transient, +ASRearr_MinorSectionHeader=style\=1|width\=40|blankLines\=0|fillChar\=-|text\=|linesBefore\=1| +ASRearr_ModifierOrder_Class=,override,native,public,private,protected,internal,static,dynamic,final +ASRearr_ModifierOrder_Function=,override,native,public,private,protected,internal,static,dynamic,final +ASRearr_ModifierOrder_Property=,override,native,public,private,protected,internal,static,dynamic,final +ASRearr_MoveImportsOutsideClass=true +ASRearr_RemoveExistingCopyrightHeaders=false +ASRearr_RemoveExistingSectionHeaders=false +ASRearr_SectionHeaderMap=\#\r\n\#Tue Apr 30 18\:56\:15 CEST 2013\r\n +ASRearr_SortGettersAndSettersWithAssociatedProperties=true +ASRearr_SortGettersAndSettersWithAssociatedStaticProperties=true +ASRearr_UseCopyrightGeneration=false +ASRearr_UseElementFunctionVisibilityOrder=true +ASRearr_UseElementOrder=true +ASRearr_UseElementPropertyVisibilityOrder=true +ASRearr_UseElementStaticFunctionVisibilityOrder=true +ASRearr_UseElementStaticPropertyVisibilityOrder=true +ASRearr_UseGlobalModifierOrder=true +ASRearr_UseImportOrder=true +ASRearr_UseMetatagOrder=false +ASRearr_UseModifierOrder_Class=true +ASRearr_UseModifierOrder_Function=true +ASRearr_UseModifierOrder_Property=true +ASRearr_UseSectionHeaders=false +ASRearr_UseSectionHeadersInMXML=false +ActionScript.keepRelativeIndentInDocComments=true +ActionScript.metatagsOnSameLineAsTargetFunction=Bindable +ActionScript.metatagsOnSameLineAsTargetProperty=Bindable, +ActionScript.spacesBeforeArguments=0 +ActionScript.spacesBeforeFormalParameters=1 +Actionscript.DocCommentKeepBlankLines=true +Actionscript.MLCommentAsteriskMode=0 +Actionscript.MLCommentHeaderOnSeparateLine=false +Actionscript.MLCommentKeepBlankLines=true +Actionscript.MLCommentReflowLines=false +Actionscript.addBracesToCases=1 +Actionscript.addBracesToConditionals=1 +Actionscript.addBracesToLoops=1 +Actionscript.advancedCRBeforeBraceSettings=2047 +Actionscript.advancedSpacesAroundEqualsInMetatags=0 +Actionscript.advancedSpacesAroundEqualsInOptionalParameters=0 +Actionscript.advancedSpacesInsideArrayDeclBrackets=1 +Actionscript.advancedSpacesInsideArrayRefBrackets=0 +Actionscript.advancedSpacesInsideLiteralBraces=1 +Actionscript.advancedSpacesInsideParensInArgumentLists=0 +Actionscript.advancedSpacesInsideParensInOtherPlaces=0 +Actionscript.advancedSpacesInsideParensInParameterLists=0 +Actionscript.advancedUseSpacesAroundEqualsInMetatags=false +Actionscript.advancedUseSpacesAroundEqualsInOptionalParameters=false +Actionscript.advancedWrappingAlignArrayItems=false +Actionscript.advancedWrappingAlignObjectItems=false +Actionscript.advancedWrappingAllArgs=false +Actionscript.advancedWrappingAllArrayItems=false +Actionscript.advancedWrappingAllObjectItems=false +Actionscript.advancedWrappingAllParms=false +Actionscript.advancedWrappingElements=165 +Actionscript.advancedWrappingEnforceMax=false +Actionscript.advancedWrappingFirstArg=false +Actionscript.advancedWrappingFirstArrayItem=false +Actionscript.advancedWrappingFirstObjectItem=false +Actionscript.advancedWrappingFirstParm=false +Actionscript.advancedWrappingGraceColumns=5 +Actionscript.advancedWrappingPreservePhrases=true +Actionscript.alignDeclEquals=false +Actionscript.alignDeclEqualsMode=1 +Actionscript.alignLineCommentsAtColumn=0 +Actionscript.alwaysGenerateIndent=false +Actionscript.blankLinesAtFunctionEnd=0 +Actionscript.blankLinesAtFunctionStart=0 +Actionscript.blankLinesBeforeClasses=1 +Actionscript.blankLinesBeforeControlStatements=0 +Actionscript.blankLinesBeforeFunctions=1 +Actionscript.blankLinesBeforeImportBlock=0 +Actionscript.blankLinesBeforeProperties=0 +Actionscript.blankLinesToKeep=0 +Actionscript.braceStyle=4 +Actionscript.breakLinesBeforeArithmeticOperator=false +Actionscript.breakLinesBeforeAssignment=false +Actionscript.breakLinesBeforeComma=false +Actionscript.breakLinesBeforeLogicalOperator=false +Actionscript.collapseSpacesForAdjacentParens=true +Actionscript.doRearrangeWhileFormatting=false +Actionscript.docCommentHangingIndentTabs=0 +Actionscript.docCommentReflow=false +Actionscript.dontIndentPackageItems=true +Actionscript.dontIndentSwitchCases=true +Actionscript.keepBlankLines=true +Actionscript.keepElseIfOnSameLine=true +Actionscript.keepSLCommentsOnColumn1=true +Actionscript.keepSpacesBeforeLineComments=false +Actionscript.leaveExtraWhitespaceAroundVarDecls=true +Actionscript.leaveSingleLineFunctions=false +Actionscript.maxLineLength=200 +Actionscript.newLineBeforeBindableFunction=true +Actionscript.newLineBeforeBindableProperty=true +Actionscript.noNewCRsBeforeBreak=false +Actionscript.noNewCRsBeforeContinue=false +Actionscript.noNewCRsBeforeExpression=false +Actionscript.noNewCRsBeforeReturn=false +Actionscript.noNewCRsBeforeThrow=false +Actionscript.putCatchOnNewLine=false +Actionscript.putElseOnNewLine=false +Actionscript.putEmptyStatementsOnNewLine=true +Actionscript.putOpenBraceOnNewLine=false +Actionscript.putWhileOnNewLine=false +Actionscript.spacesAfterColonsInDeclarations=0 +Actionscript.spacesAfterColonsInFunctions=1 +Actionscript.spacesAfterComma=1 +Actionscript.spacesAfterLabel=1 +Actionscript.spacesAroundAssignment=1 +Actionscript.spacesAroundBinarySymbolicOperator=1 +Actionscript.spacesAroundColons=1 +Actionscript.spacesBeforeColonsInDeclarations=0 +Actionscript.spacesBeforeColonsInFunctions=1 +Actionscript.spacesBeforeComma=0 +Actionscript.spacesBeforeControlOpenParen=1 +Actionscript.spacesInsideParens=0 +Actionscript.tabCountForHangingIndent=1 +Actionscript.unindentExpressionTerminators=false +Actionscript.useAdvancedWrapping=false +Actionscript.useBraceStyle=false +Actionscript.useDocCommentWrapping=false +Actionscript.useGlobalCRBeforeBrace=true +Actionscript.useGlobalSpacesAroundColons=false +Actionscript.useGlobalSpacesInsideParens=true +Actionscript.useGnuBraceIndent=false +Actionscript.useLineCommentWrapping=false +Actionscript.useMLCommentWrapping=false +Actionscript.wrapArrayDeclMode=1 +Actionscript.wrapEmbeddedXMLMode=2 +Actionscript.wrapExpressionMode=1 +Actionscript.wrapIndentStyle=1000 +Actionscript.wrapMethodCallMode=1 +Actionscript.wrapMethodDeclMode=1 +Flex.useTabs=true +MXML.KeepScriptCDataOnSameLine=false +MXML.ScriptCDataIndentTabs=1 +MXML.ScriptIndentTabs=1 +MXML.addNewlineAfterLastAttr=false +MXML.alwaysUseMaxLineLength=false +MXML.attrGroups=name\=properties|sort\=11|includeStates\=true|wrap\=54|attrs\=allowDisjointSelection,allowMultipleSelection,allowThumbOverlap,allowTrackClick,autoLayout,autoRepeat,automationName,cachePolicy,class,clipContent,condenseWhite,conversion,creationIndex,creationPolicy,currentState,data,dataDescriptor,dataProvider,dataTipFormatFunction,dayNames,defaultButton,direction,disabledDays,disabledRanges,displayedMonth,displayedYear,doubleClickEnabled,emphasized,enabled,explicitHeight,explicitMaxHeight,explicitMaxWidth,explicitMinHeight,explicitMinWidth,explicitWidth,firstDayOfWeek,focusEnabled,fontContext,height,horizontalLineScrollSize,horizontalPageScrollSize,horizontalScrollBar,horizontalScrollPolicy,horizontalScrollPosition,htmlText,icon,iconField,id,imeMode,includeInLayout,indeterminate,label,labelField,labelFunction,labelPlacement,labels,layout,lineScrollSize,listData,liveDragging,maxChars,maxHeight,maxScrollPosition,maxWidth,maxYear,maximum,measuredHeight,measuredMinHeight,measuredMinWidth,measuredWidth,menuBarItemRenderer,menuBarItems,menus,minHeight,minScrollPosition,minWidth,minYear,minimum,mode,monthNames,monthSymbol,mouseFocusEnabled,pageScrollSize,pageSize,percentHeight,percentWidth,scaleX,scaleY,scrollPosition,selectable,selectableRange,selected,selectedDate,selectedField,selectedIndex,selectedRanges,showDataTip,showRoot,showToday,sliderDataTipClass,sliderThumbClass,snapInterval,source,states,stepSize,stickyHighlighting,styleName,text,thumbCount,tickInterval,tickValues,toggle,toolTip,transitions,truncateToFit,validationSubField,value,verticalLineScrollSize,verticalPageScrollSize,verticalScrollBar,verticalScrollPolicy,verticalScrollPosition,width,x,y,yearNavigationEnabled,yearSymbol,|data\=-1|\nname\=xml_namespaces|sort\=11|includeStates\=true|wrap\=54|attrs\=xmlns,xmlns\:.*,|data\=-1|\nname\=events|sort\=11|includeStates\=true|wrap\=54|attrs\=add,added,activate,addedToStage,buttonDown,change,childAdd,childIndexChange,childRemove,clickHandler,clear,click,complete,contextMenu,copy,creationComplete,currentStateChange,currentStateChanging,cut,dataChange,deactivate,doubleClick,dragComplete,dragDrop,dragEnter,dragExit,dragOver,dragStart,effectEnd,effectStart,enterFrame,enterState,exitFrame,exitState,focusIn,focusOut,frameConstructed,hide,httpStatus,init,initialize,invalid,ioError,itemClick,itemRollOut,itemRollOver,keyDown,keyFocusChange,keyUp,menuHide,menuShow,middleClick,middleMouseDown,middleMouseUp,mouseDown,mouseUp,mouseOver,mouseMove,mouseOut,mouseFocusChange,mouseWheel,mouseDownOutside,mouseWheelOutside,move,nativeDragComplete,nativeDragDrop,nativeDragEnter,nativeDragExit,nativeDragOver,nativeDragStart,nativeDragUpdate,open,paste,preinitialize,progress,record,remove,removed,removedFromStage,render,resize,rightClick,rightMouseDown,rightMouseUp,rollOut,rollOver,scroll,securityError,selectAll,show,tabChildrenChange,tabEnabledChange,tabIndexChange,thumbDrag,thumbPress,thumbRelease,toolTipCreate,toolTipEnd,toolTipHide,toolTipShow,toolTipShown,toolTipStart,updateComplete,unload,valid,valueCommit,|data\=-1|\nname\=styles|sort\=11|includeStates\=true|wrap\=54|attrs\=backgroundAlpha,backgroundAttachment,backgroundColor,backgroundDisabledColor,backgroundImage,backgroundSize,backgroundSkin,barColor,barSkin,borderColor,borderSides,borderSkin,borderStyle,borderThickness,bottom,color,cornerRadius,dataTipOffset,dataTipPrecision,dataTipStyleName,disabledColor,disabledIcon,disabledIconColor,disabledSkin,disbledOverlayAlpha,downArrowDisabledSkin,downArrowDownSkin,downArrowOverSkin,downArrowUpSkin,downIcon,downSkin,dropShadowColor,dropShadowEnabled,errorColor,fillAlphas,fillColors,focusAlpha,focusBlendMode,focusRoundedCorners,focusSkin,focusThickness,fontAntiAliasType,fontFamily,fontGridFitType,fontSharpness,fontSize,fontStyle,fontThickness,fontWeight,fontfamily,headerColors,headerStyleName,highlightAlphas,horizontalAlign,horizontalCenter,horizontalGap,horizontalScrollBarStyleName,icon,iconColor,indeterminateMoveInterval,indeterminateSkin,itemDownSkin,itemOverSkin,itemUpSkin,kerning,labelOffset,labelStyleName,labelWidth,leading,left,letterSpacing,maskSkin,menuStyleName,nextMonthDisabledSkin,nextMonthDownSkin,nextMonthOverSkin,nextMonthSkin,nextMonthUpSkin,nextYearDisabledSkin,nextYearDownSkin,nextYearOverSkin,nextYearSkin,nextYearUpSkin,overIcon,overSkin,paddingBottom,paddingLeft,paddingRight,paddingTop,prevMonthDisabledSkin,prevMonthDownSkin,prevMonthOverSkin,prevMonthSkin,prevMonthUpSkin,prevYearDisabledSkin,prevYearDownSkin,prevYearOverSkin,prevYearSkin,prevYearUpSkin,repeatDelay,repeatInterval,right,rollOverColor,rollOverIndicatorSkin,selectedDisabledIcon,selectedDisabledSkin,selectedDownIcon,selectedDownSkin,selectedOverIcon,selectedOverSkin,selectedUpIcon,selectedUpSkin,selectionColor,selectionIndicatorSkin,shadowColor,shadowDirection,shadowDistance,showTrackHighlight,skin,slideDuration,slideEasingFunction,strokeColor,strokeWidth,textAlign,textDecoration,textIndent,textRollOverColor,textSelectedColor,themeColor,thumbDisabledSkin,thumbDownSkin,thumbIcon,thumbOffset,thumbOverSkin,thumbUpSkin,tickColor,tickLength,tickOffset,tickThickness,todayColor,todayIndicatorSkin,todayStyleName,top,tracHighlightSkin,trackColors,trackHeight,trackMargin,trackSkin,upArrowDisabledSkin,upArrowDownSkin,upArrowOverSkin,upArrowUpSkin,upIcon,upSkin,verticalAlign,verticalCenter,verticalGap,verticalScrollBarStyleName,weekDayStyleName,|data\=-1|\nname\=effects|sort\=11|includeStates\=true|wrap\=54|attrs\=addedEffect,completeEffect,creationCompleteEffect,focusInEffect,focusOutEffect,hideEffect,mouseDownEffect,mouseUpEffect,moveEffect,removedEffect,resizeEffect,rollOutEffect,rollOverEffect,showEffect,|data\=-1|\nname\=Special_Group--Other Attributes|sort\=11|includeStates\=true|wrap\=54|attrs\=|data\=-1|\n +MXML.attrWrapMode=52 +MXML.attrsPerLine=1 +MXML.attrsToKeepOnSameLine=4 +MXML.blankLinesAfterParentTags=0 +MXML.blankLinesAfterSpecificParentTags=0 +MXML.blankLinesAtCDataStart=0 +MXML.blankLinesBeforeClosingTags=0 +MXML.blankLinesBeforeComments=0 +MXML.blankLinesBeforeTags=0 +MXML.blankLinesBetweenSiblingTags=0 +MXML.doRemoveNamespacesWhileFormatting=false +MXML.indentTagClose=true +MXML.keepBlankLines=true +MXML.keepRelativeIndentInMultilineComments=true +MXML.maxLineLength=200 +MXML.onlyFormatASIfCDATABlock=false +MXML.parentTagsToHaveBlankLinesAddedAfterThem= +MXML.sortAttrData= +MXML.sortAttrMode=0 +MXML.sortExtraAttrs=false +MXML.spacesAroundEquals=0 +MXML.spacesBeforeEmptyTagEnd=0 +MXML.spacesInsideAttributeBraces=0 +MXML.tabCountForHangingIndent=1 +MXML.tagsCanFormat=mx\:List,fx\:List, +MXML.tagsCannotFormat=mx\:String,fx\:String, +MXML.tagsDoNotFormatInside=.*\:Model,.*\:XML, +MXML.tagsToHaveBlankLinesAddedBeforeThem= +MXML.tagsWithASContent=.*\:add,.*\:added,.*\:activate,.*\:addedToStage,.*\:buttonDown,.*\:change,.*\:childAdd,.*\:childIndexChange,.*\:childRemove,.*\:clickHandler,.*\:clear,.*\:click,.*\:complete,.*\:contextMenu,.*\:copy,.*\:creationComplete,.*\:currentStateChange,.*\:currentStateChanging,.*\:cut,.*\:dataChange,.*\:deactivate,.*\:doubleClick,.*\:dragComplete,.*\:dragDrop,.*\:dragEnter,.*\:dragExit,.*\:dragOver,.*\:dragStart,.*\:effectEnd,.*\:effectStart,.*\:enterFrame,.*\:enterState,.*\:exitFrame,.*\:exitState,.*\:focusIn,.*\:focusOut,.*\:frameConstructed,.*\:hide,.*\:httpStatus,.*\:init,.*\:initialize,.*\:invalid,.*\:ioError,.*\:itemClick,.*\:itemRollOut,.*\:itemRollOver,.*\:keyDown,.*\:keyFocusChange,.*\:keyUp,.*\:menuHide,.*\:menuShow,.*\:middleClick,.*\:middleMouseDown,.*\:middleMouseUp,.*\:mouseDown,.*\:mouseUp,.*\:mouseOver,.*\:mouseMove,.*\:mouseOut,.*\:mouseFocusChange,.*\:mouseWheel,.*\:mouseDownOutside,.*\:mouseWheelOutside,.*\:move,.*\:nativeDragComplete,.*\:nativeDragDrop,.*\:nativeDragEnter,.*\:nativeDragExit,.*\:nativeDragOver,.*\:nativeDragStart,.*\:nativeDragUpdate,.*\:open,.*\:paste,.*\:preinitialize,.*\:progress,.*\:record,.*\:remove,.*\:removed,.*\:removedFromStage,.*\:render,.*\:resize,.*\:rightClick,.*\:rightMouseDown,.*\:rightMouseUp,.*\:rollOut,.*\:rollOver,.*\:scroll,.*\:securityError,.*\:selectAll,.*\:show,.*\:tabChildrenChange,.*\:tabEnabledChange,.*\:tabIndexChange,.*\:thumbDrag,.*\:thumbPress,.*\:thumbRelease,.*\:toolTipCreate,.*\:toolTipEnd,.*\:toolTipHide,.*\:toolTipShow,.*\:toolTipShown,.*\:toolTipStart,.*\:updateComplete,.*\:unload,.*\:valid,.*\:valueCommit,.*\:Script, +MXML.useAttrsToKeepOnSameLine=false +MXML.useFormattingOfBoundAttributes=false +MXML.useSpacesInsideAttributeBraces=false +MXML.useTagsDoNotFormatInside=false +MXML.wrapIndentStyle=1001 +MXMLRearr_RearrangeTagOrdering=.*\:Binding,.*\:Component,.*\:Declarations,.*\:Definition,.*\:DesignLayer,.*\:Library,.*\:Metadata,.*\:Model,.*\:Private,.*\:Reparent,.*\:Script,.*\:Style,.*\:XML,.*\:XMLList,.*\:operation,.*\:request,.*\:method,.*\:arguments,.*\:states,.*\:layout,\#\#\#UnmatchedTags\#\#\#, +MXMLRearr_RearrangeWhileFormatting=false +MXMLRearr_UseRearrangeTagOrdering=false +UseProjectSettings=false +eclipse.preferences.version=1 diff --git a/spine-starling/spine-starling/.settings/org.eclipse.core.resources.prefs b/spine-starling/spine-starling/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..4ef613960 --- /dev/null +++ b/spine-starling/spine-starling/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,3 @@ +#Mon Apr 22 14:45:38 CEST 2013 +eclipse.preferences.version=1 +encoding/=utf-8 diff --git a/spine-starling/spine-starling/libs/starling.swc b/spine-starling/spine-starling/libs/starling.swc new file mode 100644 index 000000000..9c9e08f17 Binary files /dev/null and b/spine-starling/spine-starling/libs/starling.swc differ diff --git a/spine-starling/spine-starling/src/spine/SkeletonAnimationSprite.as b/spine-starling/spine-starling/src/spine/SkeletonAnimationSprite.as new file mode 100644 index 000000000..716d4f578 --- /dev/null +++ b/spine-starling/spine-starling/src/spine/SkeletonAnimationSprite.as @@ -0,0 +1,66 @@ +package spine { +import spine.AnimationState; +import spine.AnimationStateData; +import spine.SkeletonData; + +import starling.events.EnterFrameEvent; + +public class SkeletonAnimationSprite extends SkeletonSprite { + public var states:Vector. = new Vector.(); + + public function SkeletonAnimationSprite (skeletonData:SkeletonData) { + super(skeletonData); + addAnimationState(); + } + + override protected function onEnterFrame (event:EnterFrameEvent) : void { + super.onEnterFrame(event); + + var deltaTime:Number = event.passedTime * timeScale; + for each (var state:AnimationState in states) { + state.update(deltaTime); + state.apply(skeleton); + } + skeleton.updateWorldTransform(); + } + + public function addAnimationState (stateData:AnimationStateData = null) : void { + if (!stateData) + stateData = new AnimationStateData(skeleton.data); + states.push(new AnimationState(stateData)); + } + + public function setAnimationStateData (stateData:AnimationStateData, stateIndex:int = 0) : void { + if (stateIndex < 0 || stateIndex >= states.length) + throw new ArgumentError("stateIndex out of range."); + if (!stateData) + throw new ArgumentError("stateData cannot be null."); + states[stateIndex] = new AnimationState(stateData); + } + + public function setMix (fromAnimation:String, toAnimation:String, duration:Number, stateIndex:int = 0) : void { + if (stateIndex < 0 || stateIndex >= states.length) + throw new ArgumentError("stateIndex out of range."); + states[stateIndex].data.setMixByName(fromAnimation, toAnimation, duration); + } + + public function setAnimation (name:String, loop:Boolean, stateIndex:int = 0) : void { + if (stateIndex < 0 || stateIndex >= states.length) + throw new ArgumentError("stateIndex out of range."); + states[stateIndex].setAnimationByName(name, loop); + } + + public function addAnimation (name:String, loop:Boolean, delay:Number = 0, stateIndex:int = 0) : void { + if (stateIndex < 0 || stateIndex >= states.length) + throw new ArgumentError("stateIndex out of range."); + states[stateIndex].addAnimationByName(name, loop, delay); + } + + public function clearAnimation (stateIndex:int = 0) : void { + if (stateIndex < 0 || stateIndex >= states.length) + throw new ArgumentError("stateIndex out of range."); + states[stateIndex].clearAnimation(); + } +} + +} diff --git a/spine-starling/spine-starling/src/spine/SkeletonImage.as b/spine-starling/spine-starling/src/spine/SkeletonImage.as new file mode 100644 index 000000000..38d160d96 --- /dev/null +++ b/spine-starling/spine-starling/src/spine/SkeletonImage.as @@ -0,0 +1,21 @@ +package spine { + +import starling.display.Image; +import starling.textures.Texture; +import starling.utils.VertexData; + +public class SkeletonImage extends Image { + public function SkeletonImage (texture:Texture) { + super(texture); + } + + public function get vertexData () : VertexData { + return mVertexData; + } + + override public function get tinted () : Boolean { + return true; + } +} + +} diff --git a/spine-starling/spine-starling/src/spine/SkeletonSprite.as b/spine-starling/spine-starling/src/spine/SkeletonSprite.as new file mode 100644 index 000000000..c422f64fd --- /dev/null +++ b/spine-starling/spine-starling/src/spine/SkeletonSprite.as @@ -0,0 +1,207 @@ +package spine { +import flash.geom.Matrix; +import flash.geom.Point; +import flash.geom.Rectangle; + +import spine.Bone; +import spine.Skeleton; +import spine.SkeletonData; +import spine.Slot; +import spine.attachments.RegionAttachment; + +import starling.core.RenderSupport; +import starling.display.DisplayObject; +import starling.events.EnterFrameEvent; +import starling.events.Event; +import starling.utils.MatrixUtil; + +public class SkeletonSprite extends DisplayObject { + static private var tempPoint:Point = new Point(); + static private var tempMatrix:Matrix = new Matrix(); + + private var _skeleton:Skeleton; + public var timeScale:Number = 1; + + public function SkeletonSprite (skeletonData:SkeletonData) { + _skeleton = new Skeleton(skeletonData); + _skeleton.updateWorldTransform(); + + addEventListener(Event.ENTER_FRAME, onEnterFrame); + + Bone.yDown = true; + } + + protected function onEnterFrame (event:EnterFrameEvent) : void { + _skeleton.update(event.passedTime * timeScale); + } + + override public function render (support:RenderSupport, alpha:Number) : void { + var drawOrder:Vector. = skeleton.drawOrder; + for (var i:int = 0, n:int = drawOrder.length; i < n; i++) { + var slot:Slot = drawOrder[i]; + var regionAttachment:RegionAttachment = slot.attachment as RegionAttachment; + if (regionAttachment != null) { + regionAttachment.updateVertices(slot.bone); + var vertices:Vector. = regionAttachment.vertices; + var r:Number = skeleton.r * slot.r; + var g:Number = skeleton.g * slot.g; + var b:Number = skeleton.b * slot.b; + var a:Number = skeleton.a * slot.a; + + var image:SkeletonImage = regionAttachment.texture as SkeletonImage; + var vertexData:Vector. = image.vertexData.rawData; + + vertexData[0] = vertices[2]; + vertexData[1] = vertices[3]; + vertexData[2] = r; + vertexData[3] = g; + vertexData[4] = b; + vertexData[5] = a; + + vertexData[8] = vertices[4]; + vertexData[9] = vertices[5]; + vertexData[10] = r; + vertexData[11] = g; + vertexData[12] = b; + vertexData[13] = a; + + vertexData[16] = vertices[0]; + vertexData[17] = vertices[1]; + vertexData[18] = r; + vertexData[19] = g; + vertexData[20] = b; + vertexData[21] = a; + + vertexData[24] = vertices[6]; + vertexData[25] = vertices[7]; + vertexData[26] = r; + vertexData[27] = g; + vertexData[28] = b; + vertexData[29] = a; + + support.batchQuad(image, alpha, image.texture); + } + } + } + + override public function getBounds (targetSpace:DisplayObject, resultRect:Rectangle = null) : Rectangle { + var minX:Number = Number.MAX_VALUE, minY:Number = Number.MAX_VALUE; + var maxX:Number = Number.MIN_VALUE, maxY:Number = Number.MIN_VALUE; + var scaleX:Number = this.scaleX; + var scaleY:Number = this.scaleY; + var slots:Vector. = skeleton.slots; + var value:Number; + for (var i:int = 0, n:int = slots.length; i < n; i++) { + var slot:Slot = slots[i]; + var regionAttachment:RegionAttachment = slot.attachment as RegionAttachment; + if (!regionAttachment) + continue; + + regionAttachment.updateVertices(slot.bone); + var vertices:Vector. = regionAttachment.vertices; + + value = vertices[0] * scaleX; + if (value < minX) + minX = value; + if (value > maxX) + maxX = value; + + value = vertices[1] * scaleY; + if (value < minY) + minY = value; + if (value > maxY) + maxY = value; + + value = vertices[2] * scaleX; + if (value < minX) + minX = value; + if (value > maxX) + maxX = value; + + value = vertices[3] * scaleY; + if (value < minY) + minY = value; + if (value > maxY) + maxY = value; + + value = vertices[4] * scaleX; + if (value < minX) + minX = value; + if (value > maxX) + maxX = value; + + value = vertices[5] * scaleY; + if (value < minY) + minY = value; + if (value > maxY) + maxY = value; + + value = vertices[6] * scaleX; + if (value < minX) + minX = value; + if (value > maxX) + maxX = value; + + value = vertices[7] * scaleY; + if (value < minY) + minY = value; + if (value > maxY) + maxY = value; + } + + if (!resultRect) + resultRect = new Rectangle(); + + // FIXME + resultRect.setTo(0, 0, 0, 0); + return resultRect; + // No idea why the below makes rendering very small. :( Returning 0,0 0x0 renders fine?? + if (targetSpace == this) { + resultRect.x = minX; + resultRect.y = minY; + resultRect.width = maxX - minX; + resultRect.height = maxY - minY; + } else if (targetSpace == parent && rotation == 0.0) { + resultRect.x = x + minX - pivotX * scaleX; + resultRect.y = y + minY - pivotY * scaleY; + resultRect.width = (maxX - minX) * scaleX; + resultRect.height = (maxY - minY) * scaleY; + if (scaleX < 0) { + resultRect.width *= -1; + resultRect.x -= resultRect.width; + } + if (scaleY < 0) { + resultRect.height *= -1; + resultRect.y -= resultRect.height; + } + } else { + getTransformationMatrix(targetSpace, tempMatrix); + MatrixUtil.transformCoords(tempMatrix, minX, minY, tempPoint); + minX = tempPoint.x; + minY = tempPoint.y; + MatrixUtil.transformCoords(tempMatrix, maxX, maxY, tempPoint); + if (minX > tempPoint.x) { + maxX = minX; + minX = tempPoint.x; + } else + maxX = tempPoint.x; + if (minY > tempPoint.y) { + maxY = minY; + minY = tempPoint.y; + } else + maxY = tempPoint.y; + resultRect.x = minX; + resultRect.y = minY; + resultRect.width = maxX - minX; + resultRect.height = maxY - minY; + } + return resultRect; + } + + public function get skeleton () : Skeleton { + return _skeleton; + } +} + +} + diff --git a/spine-starling/spine-starling/src/spine/StarlingAtlasAttachmentLoader.as b/spine-starling/spine-starling/src/spine/StarlingAtlasAttachmentLoader.as new file mode 100644 index 000000000..618f1a912 --- /dev/null +++ b/spine-starling/spine-starling/src/spine/StarlingAtlasAttachmentLoader.as @@ -0,0 +1,39 @@ +package spine { +import spine.Bone; +import spine.Skin; +import spine.attachments.Attachment; +import spine.attachments.AttachmentLoader; +import spine.attachments.AttachmentType; +import spine.attachments.RegionAttachment; + +import starling.textures.Texture; +import starling.textures.TextureAtlas; + +public class StarlingAtlasAttachmentLoader implements AttachmentLoader { + private var atlas:TextureAtlas; + + public function StarlingAtlasAttachmentLoader (atlas:TextureAtlas) { + this.atlas = atlas; + + Bone.yDown = true; + } + + public function newAttachment (skin:Skin, type:AttachmentType, name:String) : Attachment { + if (type == AttachmentType.region) { + var regionAttachment:RegionAttachment = new RegionAttachment(name); + var texture:Texture = atlas.getTexture(name); + regionAttachment.texture = new SkeletonImage(texture); + regionAttachment.regionOffsetX = texture.frame.x; + regionAttachment.regionOffsetY = texture.frame.y; + regionAttachment.regionWidth = texture.width; + regionAttachment.regionHeight = texture.height; + regionAttachment.regionOriginalWidth = texture.width; + regionAttachment.regionOriginalHeight = texture.height; + return regionAttachment; + } + + throw new Error("Unknown attachment type: " + type); + } +} + +} diff --git a/spine-starling/spine-starling/src/spine/StarlingSkeletonJson.as b/spine-starling/spine-starling/src/spine/StarlingSkeletonJson.as new file mode 100644 index 000000000..c77f4d2bd --- /dev/null +++ b/spine-starling/spine-starling/src/spine/StarlingSkeletonJson.as @@ -0,0 +1,34 @@ +package spine { +import flash.utils.ByteArray; + +import spine.Bone; +import spine.SkeletonData; +import spine.SkeletonJson; +import spine.attachments.AttachmentLoader; + +import starling.textures.TextureAtlas; + +public class StarlingSkeletonJson { + private var json:SkeletonJson; + + /** @param object A TextureAtlas or AttachmentLoader. */ + public function StarlingSkeletonJson (object:*) { + if (object is TextureAtlas) + json = new SkeletonJson(new StarlingAtlasAttachmentLoader(object)); + else if (object is AttachmentLoader) + json = new SkeletonJson(AttachmentLoader(object)); + else + throw new Error("object must be a TextureAtlas or AttachmentLoader."); + + Bone.yDown = true; + } + + /** @param object A String or ByteArray. */ + public function readSkeletonData (object:*, name:String = null) : SkeletonData { + if (object is String) return json.readSkeletonData(String(object), name); + if (object is ByteArray) return json.readSkeletonData(object.readUTFBytes(object.length), name); + throw new Error("object must be a String or ByteArray."); + } +} + +}