betajs/betajs-flash

View on GitHub
src/js/embedding.js

Summary

Maintainability
A
1 hr
Test Coverage
Scoped.extend("module:", ["module:"], function () {
    return {
        options: {
            flashFile: "betajs-flash.swf"            
        }
    };
});

Scoped.define("module:FlashEmbedding", [
    "base:Class", "base:Events.EventsMixin", "browser:Dom", "base:Strings", "base:Functions", "base:Types", "base:Objs", "base:Ids", "base:Time",
    "base:Timers.Timer", "base:Async", "base:Tokens", "module:FlashObjectWrapper", "module:FlashClassWrapper", "browser:FlashHelper", "module:"
], function(Class, EventsMixin, Dom, Strings, Functions, Types, Objs, Ids, Time, Timer, Async, Tokens, FlashObjectWrapper, FlashClassWrapper, FlashHelper, mod, scoped) {
    return Class.extend({ scoped : scoped }, [EventsMixin, function(inherited) {
        return {

            constructor : function(container, options, flashOptions) {
                inherited.constructor.call(this);
                options = options || {};
                this.__registry = options.registry;
                this.__wrap = options.wrap;
                this.__hasEmbedding = options.hasEmbedding;
                this.__cache = {};
                this.__callbacks = {
                    ready: Functions.as_method(this.__ready, this)
                };
                this.__namespace = options.namespace ? options.namespace : ("flash_" + Tokens.generate_token());
                this.__is_ready = false;
                this.__is_suspended = false;
                this.__throttle_status = "";
                this.__ready_queue = [];
                this.__wrappers = {};
                this.__staticWrappers = {};
                window[this.__namespace] = this.__callbacks;
                flashOptions = Objs.extend(Objs.clone(mod.options, 1), Objs.extend({
                    FlashVars: {}
                }, flashOptions));
                flashOptions.FlashVars.ready = this.__namespace + ".ready";
                if (options.debug)
                    flashOptions.FlashVars.debug = true;
                if (!container) {
                    this.__helper_container = true;
                    container = document.createElement("div");
                    container.style.width = "1px";
                    container.style.height = "1px";
                    container.style.visibility = "hidden";
                    container.style.position = "absolute";
                    container.style.zIndex = -1;
                    document.body.appendChild(container);
                }
                this.__container = Dom.unbox(container);
                this.__embedding = options.hasEmbedding ? FlashHelper.getFlashObject(this.__container) : FlashHelper.embedFlashObject(this.__container, flashOptions);
                this.__suspendedTimer = this.auto_destroy(new Timer({
                    delay: 50,
                    context: this,
                    fire: this.__suspendedCheck
                }));
            },
            
            destroy: function () {
                try {
                    window[this.__namespace] = null;
                    delete window[this.__namespace];
                } catch (e) {}
                Objs.iter(this.__wrappers, function (wrapper) {
                    wrapper.weakDestroy();
                });
                Objs.iter(this.__staticWrappers, function (wrapper) {
                    wrapper.weakDestroy();
                });
                if (!this.__hasEmbedding) {
                    this.__container.innerHTML = "";
                    if (this.__helper_container)
                        this.__container.parentNode.removeChild(this.__container);
                }
                inherited.destroy.call(this);
            },
            
            registerCallback: function (callback, context) {
                var value = "__FLASHCALLBACK__" + Ids.uniqueId();
                var self = this;
                this.__callbacks[value] = function () {
                    callback.apply(context || self, Objs.map(Functions.getArguments(arguments), self.unserialize, self));
                };
                return value;
            },
            
            unregisterCallback: function (value) {
                delete this.__callbacks[value];
            },
            
            serialize : function (value) {
                if (Types.is_array(value))
                    return Objs.map(value, this.serialize, this);
                if (Types.is_function(value))
                    value = this.registerCallback(value);
                if (Types.is_string(value)) {
                    if (Strings.starts_with(value, "__FLASHCALLBACK__"))
                        value = this.__namespace + "." + value;
                }
                if (FlashObjectWrapper.is_instance_of(value))
                    value = value.__ident;
                if (FlashClassWrapper.is_instance_of(value))
                    value = value.__type;
                return value;
            },

            unserialize : function(value) {
                if (Types.is_string(value) && Strings.starts_with(value, "__FLASHERR__"))
                    throw Strings.strip_start(value, "__FLASHERR__");
                 if (Types.is_string(value) && Strings.starts_with(value, "__FLASHOBJ__")) {
                     if (this.__wrap) {
                        var type = Strings.strip_start(value, "__FLASHOBJ__");
                        type = Strings.splitFirst(type, "__").head.replace("::", ".");
                        if (type.toLowerCase() != "object") {
                            if (!(value in this.__wrappers))
                                this.__wrappers[value] = new FlashObjectWrapper(this, value, type);
                            return this.__wrappers[value];
                        }
                     }
                }
                return value;
            },

            invoke : function(method, args) {
                if (!this.__is_ready)
                    throw "Flash is not ready yet";
                args = args || [];
                return this.unserialize(this.__embedding[method].apply(this.__embedding, this.serialize(Functions.getArguments(args))));
            },

            invokeCached : function(method, args) {
                args = args || [];
                var key = method + "__" + Functions.getArguments(args).join("__");
                if (!(key in this.__cache))
                    this.__cache[key] = this.invoke.call(this, method, args);
                return this.__cache[key];
            },
            
            getClass: function (className) {
                if (!this.__wrap)
                    return null;
                if (!(className in this.__staticWrappers))
                    this.__staticWrappers[className] = new FlashClassWrapper(this, className);
                return this.__staticWrappers[className];
            },
            
            newObject: function () {
                return this.flashCreate.apply(this, arguments);
            },
            
            newCallback: function () {
                return Types.is_string(arguments[0]) ? this.flashCreateCallbackObject.apply(this, arguments) : this.flashCreateCallbackFunction.apply(this, arguments);
            },
            
            flashCreateCallbackObject : function() {
                return this.invoke("create_callback_object", arguments);
            },
            
            flashCreateCallbackFunction : function() {
                return this.invoke("create_callback_function", arguments);
            },

            flashCreate : function() {
                return this.invoke("create", arguments);
            },

            flashDestroy : function () {
                return this.invoke("destroy", arguments);
            },

            flashCall : function() {
                return this.invoke("call", arguments);
            },

            flashCallVoid : function() {
                return this.invoke("call_void", arguments);
            },

            flashGet : function() {
                return this.invoke("get", arguments);
            },

            flashSet : function() {
                return this.invoke("set", arguments);
            },

            flashStaticCall : function() {
                return this.invoke("static_call", arguments);
            },

            flashStaticCallVoid : function() {
                return this.invoke("static_call_void", arguments);
            },

            flashGetStatic : function() {
                return this.invoke("get_static", arguments);
            },

            flashSetStatic : function() {
                return this.invoke("set_static", arguments);
            },

            flashMain : function() {
                return this.invokeCached("main");
            },
            
            __suspendedCheck: function () {
                if (this.__is_ready) {
                    var test = Time.now();
                    var tries = 10;
                    while (tries > 0) {
                        try {
                            if (this.__embedding.echo(test) == test)
                                break;
                        } catch (e) {}
                        tries--;
                    }
                    var result = tries === 0;
                    if (result !== this.__is_suspended) {
                        this.__is_suspended = result;
                        this.trigger(result ? "suspended" : "resumed");
                    }
                    try {
                        var status = this.__embedding.throttle();
                        var changed = status !== this.__throttle_status;
                        this.__throttle_status = status;
                        if (changed)
                            this.trigger(this.isThrottled() ? "throttled" : "unthrottled");
                    } catch (e) {}
                }
            },
            
            isSuspended: function () {
                return this.__is_ready && this.__is_suspended;
            },

            isThrottled: function () {
                return this.__throttle_status === "throttle";
            },
            
            __ready: function () {
                if (!this.__is_ready) {
                    this.__is_ready = true;
                    this.__is_suspended = false;
                    Async.eventually(function () {
                        Objs.iter(this.__ready_queue, function (entry) {
                            entry.callback.call(entry.context || this);
                        }, this);
                    }, this);
                    this.trigger("ready");
                }
            },

            ready : function(callback, context) {
                if (this.__is_ready) {
                    Async.eventually(function () {
                        callback.call(context || this);
                    }, this);
                } else {
                    this.__ready_queue.push({
                        callback: callback,
                        context: context
                    });
                }
            }

        };
    }]);
});