<commit_before>//
// $Id$

package com.threerings.util {

import flash.display.Loader;
import flash.display.LoaderInfo;

import flash.events.AsyncErrorEvent;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;

import flash.net.URLRequest;

import flash.system.ApplicationDomain;
import flash.system.LoaderContext;

import flash.utils.ByteArray;
import flash.utils.Dictionary;

/**
 * Easy loader for many things, including managing multiple downloads.
 * More documentation coming.
 */
public class MultiLoader
{
    /**
     * Load one or more sources and return DisplayObjects.
     *
     * @param sources an Array, Dictionary, or Object containing sources as values, or a single
     * source value. The sources may be Strings (representing urls), URLRequests, ByteArrays,
     * or a Class that can be instantiated to become a URLRequest or ByteArray. Note that
     * the format of your sources Object dictates the format of the return Object.
     * @param completeCallback the function to call when complete. The signature should be:
     * <code>function (value :Object) :void</code>. Note that the structure of the return Object
     * is dictated by the sources parameter. If you pass in an Array, you get your results
     * in an Array. If you use a Dictionary or Object, the results will be returned as the same,
     * with the same keys used in sources now pointing to the results. If your sources parameter
     * was just a single source (like a String) then the result will just be a single result,
     * like a DisplayObject. Each result will be a DisplayObject or an Error
     * describing the problem.
     * @param forEach if true, each value or error will be returned as soon as possible. The values
     * or errors will be returned directly to the completeCallback. Any keys are lost, so you
     * probably only want to use this with an Array sources.
     * @param appDom the ApplicationDomain in which to load the contents, or null to specify
     * that it should load in a child of the current ApplicationDomain.
     * 
     * @example Load one embed, add it as a child.
     * <listing version="3.0">
     * MultiLoader.getContents(EMBED_CONSTANT, addChild);
     * </listing>
     *
     * @example Load 3 embeds, add them as children.
     * <listing version="3.0">
     * MultiLoader.getContents([EMBED1, EMBED2, EMBED3], addChild, true);
     * </listing>
     *
     * @example Load multiple URLs, have the contents returned to the result function one at
     * a time.
     * <listing version="3.0">
     * function handleComplete (result :Object) :void {
     *    // process a result here. Result may be a DisplayObject or an Error.
     * };
     * 
     * var obj :Object = {
     *     key1: "http://somehost.com/someImage.gif",
     *     key2: "http://somehost.com/someOtherImage.gif"
     * };
     * 
     * MultiLoader.getContents(obj, handleComplete, true);
     * </listing>
     * 
     * @example Load 3 embeds, wait to handle them until they're all loaded.
     * <listing version="3.0">
     * function handleComplete (results :Array) :void {
     *     // process results here
     * };
     *
     * MultiLoader.getContents([EMBED1, EMBED2, EMBED3], handleComplete);
     * </listing>
     */
    public static function getContents (
        sources :Object, completeCallback :Function, forEach :Boolean = false,
        appDom :ApplicationDomain = null) :void
    {
        var complete :Function = function (retval :Object) :void {
            completeCallback(processProperty(retval, Loader, "content"));
        };
        getLoaders(sources, complete, forEach, appDom);
    }

    /**
     * Exactly like getContents() only it returns the Loader objects rather than their contents.
     *
     * @example Advanced usage: Loading classes.
     * <listing version="3.0">
     * // A holder for new classes, created as a child of the system domain.
     * var appDom :ApplicationDomain = new ApplicationDomain(null);
     * <br/>
     * function handleComplete (results :Object) :void {
     *     // now we can retrieve classes
     *     var clazz :Class = appDom.getDefinition("com.package.SomeClass") as Class;
     * }
     * <br/>
     * // load all the classes contained in the specified sources
     * MultiLoader.getLoaders([EMBED, "http://site.com/pack.swf"], handleComplete, false, appDom);
     * <br/>
     * [Embed(source="resource.swf", mimeType="application/octet-stream")]
     * private static const EMBED :Class;
     * </listing>
     *
     * @see getContents()
     */
    public static function getLoaders (
        sources :Object, completeCallback :Function, forEach :Boolean = false,
        appDom :ApplicationDomain = null) :void
    {
        var generator :Function = function (source :*) :Object {
            // first transform common sources to their more useful nature
            if (source is String) {
                source = new URLRequest(String(source));
            } else if (source is Class) {
                // it's probably a ByteArray from an Embed, but don't cast it
                source = new (source as Class)();
            }
            var l :Loader = new Loader();
            var lc :LoaderContext = new LoaderContext(false, appDom);
            try {
                Object(lc)["allowLoadBytesCodeExecution"] = true; // AIR compatibility
            } catch (err :Error) { /* We must not be in AIR! */ }
            // now we only need handle the two cases
            if (source is URLRequest) {
                l.load(URLRequest(source), lc);
            } else if (source is ByteArray) {
                l.loadBytes(ByteArray(source), lc);
            } else {
                return new Error("Unknown source: " + source);
            }
            return l.contentLoaderInfo;
        };

        var complete :Function = function (retval :Object) :void {
            completeCallback(processProperty(retval, LoaderInfo, "loader"));
        };

        new MultiLoader(sources, complete, generator, forEach);
    }

    /**
     * Coordinate loading some asynchronous objects.
     *
     * @param sources An Array, Dictionary, or Object of sources, or just a single source.
     * @param completeCallack the function to call when complete.
     * @param generatorFunciton a function to call to generate the IEventDispatchers, or
     * null if the source values are already ready to go.
     * @param forEach whether to call the completeCallback for each source, or all-at-once at
     * the end. If forEach is used, keys will never be returned.
     * @param isCompleteCheckFn a function to attempt to call on the dispatcher to see if
     * it's already complete after generation.
     * @param errorTypes an Array of event types that will be dispatched by the loader.
     * If unspecifed, all the normal error event types are used.
     * @param completeType, the event complete type. If unspecifed @default Event.COMPLETE.
     */
    public function MultiLoader (
        sources :Object, completeCallback :Function, generatorFn :Function = null,<commit_msg>Added loadClasses(), for convenience of the common pattern of loading stuff into an ApplicationDomain.<commit_after>//
// $Id$

package com.threerings.util {

import flash.display.Loader;
import flash.display.LoaderInfo;

import flash.events.AsyncErrorEvent;
import flash.events.ErrorEvent;
import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.SecurityErrorEvent;

import flash.net.URLRequest;

import flash.system.ApplicationDomain;
import flash.system.LoaderContext;

import flash.utils.ByteArray;
import flash.utils.Dictionary;

/**
 * Easy loader for many things, including managing multiple downloads.
 * More documentation coming.
 */
public class MultiLoader
{
    /**
     * Load one or more sources and return DisplayObjects.
     *
     * @param sources an Array, Dictionary, or Object containing sources as values, or a single
     * source value. The sources may be Strings (representing urls), URLRequests, ByteArrays,
     * or a Class that can be instantiated to become a URLRequest or ByteArray. Note that
     * the format of your sources Object dictates the format of the return Object.
     * @param completeCallback the function to call when complete. The signature should be:
     * <code>function (value :Object) :void</code>. Note that the structure of the return Object
     * is dictated by the sources parameter. If you pass in an Array, you get your results
     * in an Array. If you use a Dictionary or Object, the results will be returned as the same,
     * with the same keys used in sources now pointing to the results. If your sources parameter
     * was just a single source (like a String) then the result will just be a single result,
     * like a DisplayObject. Each result will be a DisplayObject or an Error
     * describing the problem.
     * @param forEach if true, each value or error will be returned as soon as possible. The values
     * or errors will be returned directly to the completeCallback. Any keys are lost, so you
     * probably only want to use this with an Array sources.
     * @param appDom the ApplicationDomain in which to load the contents, or null to specify
     * that it should load in a child of the current ApplicationDomain.
     * 
     * @example Load one embed, add it as a child.
     * <listing version="3.0">
     * MultiLoader.getContents(EMBED_CONSTANT, addChild);
     * </listing>
     *
     * @example Load 3 embeds, add them as children.
     * <listing version="3.0">
     * MultiLoader.getContents([EMBED1, EMBED2, EMBED3], addChild, true);
     * </listing>
     *
     * @example Load multiple URLs, have the contents returned to the result function one at
     * a time.
     * <listing version="3.0">
     * function handleComplete (result :Object) :void {
     *    // process a result here. Result may be a DisplayObject or an Error.
     * };
     * 
     * var obj :Object = {
     *     key1: "http://somehost.com/someImage.gif",
     *     key2: "http://somehost.com/someOtherImage.gif"
     * };
     * 
     * MultiLoader.getContents(obj, handleComplete, true);
     * </listing>
     * 
     * @example Load 3 embeds, wait to handle them until they're all loaded.
     * <listing version="3.0">
     * function handleComplete (results :Array) :void {
     *     // process results here
     * };
     *
     * MultiLoader.getContents([EMBED1, EMBED2, EMBED3], handleComplete);
     * </listing>
     */
    public static function getContents (
        sources :Object, completeCallback :Function, forEach :Boolean = false,
        appDom :ApplicationDomain = null) :void
    {
        var complete :Function = function (retval :Object) :void {
            completeCallback(processProperty(retval, Loader, "content"));
        };
        getLoaders(sources, complete, forEach, appDom);
    }

    /**
     * Exactly like getContents() only it returns the Loader objects rather than their contents.
     *
     * @example Advanced usage: Loading classes.
     * <listing version="3.0">
     * // A holder for new classes, created as a child of the system domain.
     * var appDom :ApplicationDomain = new ApplicationDomain(null);
     * <br/>
     * function handleComplete (results :Object) :void {
     *     // now we can retrieve classes
     *     var clazz :Class = appDom.getDefinition("com.package.SomeClass") as Class;
     * }
     * <br/>
     * // load all the classes contained in the specified sources
     * MultiLoader.getLoaders([EMBED, "http://site.com/pack.swf"], handleComplete, false, appDom);
     * <br/>
     * [Embed(source="resource.swf", mimeType="application/octet-stream")]
     * private static const EMBED :Class;
     * </listing>
     *
     * @see getContents()
     */
    public static function getLoaders (
        sources :Object, completeCallback :Function, forEach :Boolean = false,
        appDom :ApplicationDomain = null) :void
    {
        var generator :Function = function (source :*) :Object {
            // first transform common sources to their more useful nature
            if (source is String) {
                source = new URLRequest(String(source));
            } else if (source is Class) {
                // it's probably a ByteArray from an Embed, but don't cast it
                source = new (source as Class)();
            }
            var l :Loader = new Loader();
            var lc :LoaderContext = new LoaderContext(false, appDom);
            try {
                Object(lc)["allowLoadBytesCodeExecution"] = true; // AIR compatibility
            } catch (err :Error) { /* We must not be in AIR! */ }
            // now we only need handle the two cases
            if (source is URLRequest) {
                l.load(URLRequest(source), lc);
            } else if (source is ByteArray) {
                l.loadBytes(ByteArray(source), lc);
            } else {
                return new Error("Unknown source: " + source);
            }
            return l.contentLoaderInfo;
        };

        var complete :Function = function (retval :Object) :void {
            completeCallback(processProperty(retval, LoaderInfo, "loader"));
        };

        new MultiLoader(sources, complete, generator, forEach);
    }

    /**
     * Loads classes into the specified ApplicationDomain.
     * The complete callback receives either the same ApplicationDomain or no arguments.
     */
    public static function loadClasses (
        sources :Object, appDom :ApplicationDomain, completeCallback :Function) :void
    {
        var complete :Function = function (retval :Object) :void {
            switch (completeCallback.length) {
            default:
                completeCallback();
                break;
            case 1:
                completeCallback(appDom);
                break;
            }
        };

        getLoaders(sources, complete, false, appDom);
    }

    /**
     * Coordinate loading some asynchronous objects.
     *
     * @param sources An Array, Dictionary, or Object of sources, or just a single source.
     * @param completeCallack the function to call when complete.
     * @param generatorFunciton a function to call to generate the IEventDispatchers, or
     * null if the source values are already ready to go.
     * @param forEach whether to call the completeCallback for each source, or all-at-once at
     * the end. If forEach is used, keys will never be returned.
     * @param isCompleteCheckFn a function to attempt to call on the dispatcher to see if
     * it's already complete after generation.
     * @param errorTypes an Array of event types that will be dispatched by the loader.
     * If unspecifed, all the normal error event types are used.
     * @param completeType, the event complete type. If unspecifed @default Event.COMPLETE.
     */
    public function MultiLoader (
        sources :Object, completeCallback :Function, generatorFn :Function = null,
{'fraction_non_alphanumeric': 0.08813447593935399, 'fraction_numerical': 0.002702702702702703, 'mean_word_length': 2.573851590106007, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4614569', 'n_tokens_mistral': 4012, 'n_tokens_neox': 3918, 'n_words': 1568}