////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package org.apache.flex.utils
{

import flash.display.DisplayObject;

import org.apache.flex.core.IBead;
import org.apache.flex.core.IContainer;
import org.apache.flex.core.IDocument;
import org.apache.flex.core.IMXMLDocument;
import org.apache.flex.core.IParent;
import org.apache.flex.core.IStrand;
import org.apache.flex.events.Event;
import org.apache.flex.events.IEventDispatcher;

/**
 *  The MXMLDataInterpreter class is the class that interprets the
 *  encoded information generated by the compiler that describes
 *  the contents of an MXML document.
 *
 *  @langversion 3.0
 *  @playerversion Flash 10.2
 *  @playerversion AIR 2.6
 *  @productversion FlexJS 0.0
 */
public class MXMLDataInterpreter
{
    /**
     *  Constructor.  All methods are static so should not be instantiated.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
    public function MXMLDataInterpreter()
    {
        super();
    }

    /**
     *  Generates an object based on the encoded data.
     *
     *  @param document The MXML document.  If the object has an id
     *  it will be assigned in this document in this method.
     *  @param data The encoded data.
     *  @return The object.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
    public static function generateMXMLObject(document:Object, data:Array):Object
    {
        var i:int = 0;
        var cls:Class = data[i++];
        var comp:Object = new cls();

        if (comp is IStrand)
            initializeStrandBasedObject(document, null, comp, data, i);
        else
        {
            var m:int;
            var j:int;
            var name:String;
            var simple:*;
            var value:Object;
            var id:String;

            m = data[i++]; // num props
            for (j = 0; j < m; j++)
            {
                name = data[i++];
                simple = data[i++];
                value = data[i++];
                if (simple == null)
                    value = generateMXMLArray(document, null, value as Array);
                else if (simple == false)
                    value = generateMXMLObject(document, value as Array);
                if (name == "id")
                {
                    document[value] = comp;
                    id = value as String;
                }
                else if (name == "_id")
                {
                    document[value] = comp;
                    id = value as String;
                    continue; // skip assignment to comp
                }
                comp[name] = value;
            }
            if (comp is IDocument)
                comp.setDocument(document, id);
        }
        return comp;
    }

    /**
     *  Generates an Array of objects based on the encoded data.
     *
     *  @param document The MXML document.  If the object has an id
     *  it will be assigned in this document in this method.
     *  @param parent The parent for any display objects encoded in the array.
     *  @param data The encoded data.
     *  @return The Array.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
    public static function generateMXMLArray(document:Object, parent:IParent, data:Array):Array
    {
        var comps:Array = [];

        var n:int = data.length;
        var i:int = 0;
        while (i < n)
        {
            var cls:Class = data[i++];
            var comp:Object = new cls();

            i = initializeStrandBasedObject(document, parent, comp, data, i);

            comps.push(comp);
        }
        return comps;
    }

    private static function initializeStrandBasedObject(document:Object, parent:IParent, comp:Object, data:Array, i:int):int
    {
        var m:int;
        var j:int;
        var name:String;
        var simple:*;
        var value:Object;
        var id:String = null;

        m = data[i++]; // num props
        if (m > 0 && data[0] == "model")
        {
            m--;
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(document, parent, value as Array);
            else if (simple == false)
                value = generateMXMLObject(document, value as Array);
            comp[name] = value;
            if (value is IBead && comp is IStrand)
                IStrand(comp).addBead(value as IBead);
        }
        var beadOffset:int = i + (m - 1) * 3;
        //if (beadOffset >= -1)
        //    trace(beadOffset, data[beadOffset]);
        if (m > 0 && data[beadOffset] == "beads")
        {
            m--;
        }
        else
            beadOffset = -1;
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(document, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(document, value as Array);
            if (name == "id")
                id = value as String;
            if (name == "document" && !comp.document)
                comp.document = document;
            else if (name == "_id")
                id = value as String; // and don't assign to comp
            else if (name == "id")
            {
                // not all objects have to have their own id property
                try {
                    comp["id"] = value;
                } catch (e:Error)
                {

                }
            }
            else
                comp[name] = value;
        }
        if (beadOffset > -1)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(document, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(document, value as Array);
            comp[name] = value;
        }
        m = data[i++]; // num styles
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(document, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(document, value as Array);
            comp.setStyle(name, value);
        }

        m = data[i++]; // num effects
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(document, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(document, value as Array);
            comp.setStyle(name, value);
        }

        m = data[i++]; // num events
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            value = data[i++];
            comp.addEventListener(name, value);
        }

        var children:Array = data[i++];
        if (children && comp is IMXMLDocument)
        {
            comp.setMXMLDescriptor(document, children);
        }
        if (parent && comp is DisplayObject)
        {
            parent.addElement(comp, !(comp is IContainer));
        }

        if (children)
        {
            if (!(comp is IMXMLDocument))
            {
                generateMXMLInstances(document, comp as IParent, children);

                // maybe we can remove this.  All IContainers should be IMXMLDocuments?
                if (comp is IContainer)
                {
                    IContainer(comp).childrenAdded();
                }
            }
        }

        if (id)
            document[id] = comp;

        if (comp is IDocument)
            comp.setDocument(document, id);

        return i;
    }

    /**
     *  Generates the instances of objects in an MXML document based on the encoded data.
     *
     *  @param document The MXML document.  If the object has an id
     *  it will be assigned in this document in this method.
     *  @param parent The parent for any display objects encoded in the array.
     *  @param data The encoded data.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
    public static function generateMXMLInstances(document:Object, parent:IParent, data:Array):void
    {
		if (!data) return;

        generateMXMLArray(document, parent, data);
    }

    /**
     *  Generates the properties of the top-level object in an MXML document
     *  based on the encoded data.  This basically means setting the attributes
     *  found on the tag and child tags that aren't in the default property.
     *
     *  @param host The MXML document.  If the object has an id
     *  it will be assigned in this document in this method.
     *  @param data The encoded data.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
    public static function generateMXMLProperties(host:Object, data:Array):void
    {
		if (!data) return;

        var i:int = 0;
        var m:int;
        var j:int;
        var name:String;
        var simple:*;
        var value:Object;
        var id:String = null;

        m = data[i++]; // num props
        var beadOffset:int = i + (m - 1) * 3;
        //if (beadOffset >= -1)
        //      (beadOffset, data[beadOffset]);
        if (m > 0 && data[beadOffset] == "beads")
        {
            m--;
        }
        else
            beadOffset = -1;
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(host, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(host, value as Array);
            if (name == "id")
                id = value as String;
            if (name == "_id")
                id = value as String; // and don't assign
            else
                host[name] = value;
        }
        if (beadOffset > -1)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(host, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(host, value as Array);
            host[name] = value;
        }
        m = data[i++]; // num styles
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(host, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(host, value as Array);
            host[name] = value;
        }

        m = data[i++]; // num effects
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(host, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(host, value as Array);
            host[name] = value;
        }

        m = data[i++]; // num events
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            value = data[i++];
            host.addEventListener(name, value as Function);
        }

    }

}
}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package org.apache.flex.utils
{

import flash.display.DisplayObject;

import org.apache.flex.core.IBead;
import org.apache.flex.core.IContainer;
import org.apache.flex.core.IDocument;
import org.apache.flex.core.IMXMLDocument;
import org.apache.flex.core.IParent;
import org.apache.flex.core.IStrand;
import org.apache.flex.events.Event;
import org.apache.flex.events.IEventDispatcher;

/**
 *  The MXMLDataInterpreter class is the class that interprets the
 *  encoded information generated by the compiler that describes
 *  the contents of an MXML document.
 *
 *  @langversion 3.0
 *  @playerversion Flash 10.2
 *  @playerversion AIR 2.6
 *  @productversion FlexJS 0.0
 */
public class MXMLDataInterpreter
{
    /**
     *  Constructor.  All methods are static so should not be instantiated.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
    public function MXMLDataInterpreter()
    {
        super();
    }

    /**
     *  Generates an object based on the encoded data.
     *
     *  @param document The MXML document.  If the object has an id
     *  it will be assigned in this document in this method.
     *  @param data The encoded data.
     *  @return The object.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
    public static function generateMXMLObject(document:Object, data:Array):Object
    {
        var i:int = 0;
        var cls:Class = data[i++];
        var comp:Object = new cls();

        if (comp is IStrand)
            initializeStrandBasedObject(document, null, comp, data, i);
        else
        {
            var m:int;
            var j:int;
            var name:String;
            var simple:*;
            var value:Object;
            var id:String;

            m = data[i++]; // num props
            for (j = 0; j < m; j++)
            {
                name = data[i++];
                simple = data[i++];
                value = data[i++];
                if (simple == null)
                    value = generateMXMLArray(document, null, value as Array);
                else if (simple == false)
                    value = generateMXMLObject(document, value as Array);
                if (name == "id")
                    id = value as String;
                if (name == "document" && !comp.document)
                    comp.document = document;
                else if (name == "_id")
                    id = value as String; // and don't assign to comp
                else if (name == "id")
                {
                    // not all objects have to have their own id property
                    try {
                        comp["id"] = value;
                    } catch (e:Error)
                    {

                    }
                }
                else
                    comp[name] = value;
            }
            if (id)
                document[id] = comp;

            if (comp is IDocument)
                comp.setDocument(document, id);
        }
        return comp;
    }

    /**
     *  Generates an Array of objects based on the encoded data.
     *
     *  @param document The MXML document.  If the object has an id
     *  it will be assigned in this document in this method.
     *  @param parent The parent for any display objects encoded in the array.
     *  @param data The encoded data.
     *  @return The Array.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
    public static function generateMXMLArray(document:Object, parent:IParent, data:Array):Array
    {
        var comps:Array = [];

        var n:int = data.length;
        var i:int = 0;
        while (i < n)
        {
            var cls:Class = data[i++];
            var comp:Object = new cls();

            i = initializeStrandBasedObject(document, parent, comp, data, i);

            comps.push(comp);
        }
        return comps;
    }

    private static function initializeStrandBasedObject(document:Object, parent:IParent, comp:Object, data:Array, i:int):int
    {
        var m:int;
        var j:int;
        var name:String;
        var simple:*;
        var value:Object;
        var id:String = null;

        m = data[i++]; // num props
        if (m > 0 && data[0] == "model")
        {
            m--;
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(document, parent, value as Array);
            else if (simple == false)
                value = generateMXMLObject(document, value as Array);
            comp[name] = value;
            if (value is IBead && comp is IStrand)
                IStrand(comp).addBead(value as IBead);
        }
        var beadOffset:int = i + (m - 1) * 3;
        //if (beadOffset >= -1)
        //    trace(beadOffset, data[beadOffset]);
        if (m > 0 && data[beadOffset] == "beads")
        {
            m--;
        }
        else
            beadOffset = -1;
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(document, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(document, value as Array);
            if (name == "id")
                id = value as String;
            if (name == "document" && !comp.document)
                comp.document = document;
            else if (name == "_id")
                id = value as String; // and don't assign to comp
            else if (name == "id")
            {
                // not all objects have to have their own id property
                try {
                    comp["id"] = value;
                } catch (e:Error)
                {

                }
            }
            else
                comp[name] = value;
        }
        if (beadOffset > -1)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(document, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(document, value as Array);
            comp[name] = value;
        }
        m = data[i++]; // num styles
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(document, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(document, value as Array);
            comp.setStyle(name, value);
        }

        m = data[i++]; // num effects
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(document, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(document, value as Array);
            comp.setStyle(name, value);
        }

        m = data[i++]; // num events
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            value = data[i++];
            comp.addEventListener(name, value);
        }

        var children:Array = data[i++];
        if (children && comp is IMXMLDocument)
        {
            comp.setMXMLDescriptor(document, children);
        }
        if (parent && comp is DisplayObject)
        {
            parent.addElement(comp, !(comp is IContainer));
        }

        if (children)
        {
            if (!(comp is IMXMLDocument))
            {
                generateMXMLInstances(document, comp as IParent, children);

                // maybe we can remove this.  All IContainers should be IMXMLDocuments?
                if (comp is IContainer)
                {
                    IContainer(comp).childrenAdded();
                }
            }
        }

        if (id)
            document[id] = comp;

        if (comp is IDocument)
            comp.setDocument(document, id);

        return i;
    }

    /**
     *  Generates the instances of objects in an MXML document based on the encoded data.
     *
     *  @param document The MXML document.  If the object has an id
     *  it will be assigned in this document in this method.
     *  @param parent The parent for any display objects encoded in the array.
     *  @param data The encoded data.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
    public static function generateMXMLInstances(document:Object, parent:IParent, data:Array):void
    {
		if (!data) return;

        generateMXMLArray(document, parent, data);
    }

    /**
     *  Generates the properties of the top-level object in an MXML document
     *  based on the encoded data.  This basically means setting the attributes
     *  found on the tag and child tags that aren't in the default property.
     *
     *  @param host The MXML document.  If the object has an id
     *  it will be assigned in this document in this method.
     *  @param data The encoded data.
     *
     *  @langversion 3.0
     *  @playerversion Flash 10.2
     *  @playerversion AIR 2.6
     *  @productversion FlexJS 0.0
     */
    public static function generateMXMLProperties(host:Object, data:Array):void
    {
		if (!data) return;

        var i:int = 0;
        var m:int;
        var j:int;
        var name:String;
        var simple:*;
        var value:Object;
        var id:String = null;

        m = data[i++]; // num props
        var beadOffset:int = i + (m - 1) * 3;
        //if (beadOffset >= -1)
        //      (beadOffset, data[beadOffset]);
        if (m > 0 && data[beadOffset] == "beads")
        {
            m--;
        }
        else
            beadOffset = -1;
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(host, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(host, value as Array);
            if (name == "id")
                id = value as String;
            if (name == "_id")
                id = value as String; // and don't assign
            else
                host[name] = value;
        }
        if (beadOffset > -1)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(host, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(host, value as Array);
            host[name] = value;
        }
        m = data[i++]; // num styles
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(host, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(host, value as Array);
            host[name] = value;
        }

        m = data[i++]; // num effects
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            simple = data[i++];
            value = data[i++];
            if (simple == null)
                value = generateMXMLArray(host, null, value as Array);
            else if (simple == false)
                value = generateMXMLObject(host, value as Array);
            host[name] = value;
        }

        m = data[i++]; // num events
        for (j = 0; j < m; j++)
        {
            name = data[i++];
            value = data[i++];
            host.addEventListener(name, value as Function);
        }

    }

}
}

fix id handling on objects

fix id handling on objects


package goplayer
{
  public class ConfigurationParser
  {
    public static const DEFAULT_API_URL : String
      = "http://staging.streamio.se/api"
    public static const DEFAULT_SKIN_URL : String = "goplayer-skin.swf"
    public static const DEFAULT_TRACKER_ID : String = "global"

    public static const VALID_PARAMETERS : Array = [
      "api", "tracker", "skin", "video", "bitrate",
      "enablertmp", "autoplay", "loop",
      "skin:showchrome", "skin:showtitle" ]

    private const result : Configuration = new Configuration

    private var parameters : Object
    private var originalParameterNames : Object

    public function ConfigurationParser
      (parameters : Object, originalParameterNames : Object)
    {
      this.parameters = parameters
      this.originalParameterNames = originalParameterNames
    }

    public function execute() : void
    {
      result.apiURL = getString("api", DEFAULT_API_URL)
      result.trackerID = getString("tracker", DEFAULT_TRACKER_ID)
      result.skinURL = getString("skin", DEFAULT_SKIN_URL)
      result.movieID = getString("video", null)
      result.bitratePolicy = getBitratePolicy("bitrate", BitratePolicy.BEST)
      result.enableRTMP = getBoolean("enablertmp", true)
      result.enableAutoplay = getBoolean("autoplay", false)
      result.enableLooping = getBoolean("loop", false)
      result.enableChrome = getBoolean("skin:showchrome", true)
      result.enableTitle = getBoolean("skin:showtitle", true)
    }

    public static function parse(parameters : Object) : Configuration
    {
      const normalizedParameters : Object = {}
      const originalParameterNames : Object = {}

      for (var name : String in parameters)
        if (VALID_PARAMETERS.indexOf(normalize(name)) == -1)
          reportUnknownParameter(name)
        else
          normalizedParameters[normalize(name)] = parameters[name],
            originalParameterNames[normalize(name)] = name

      const parser : ConfigurationParser = new ConfigurationParser
        (normalizedParameters, originalParameterNames)

      parser.execute()

      return parser.result
    }

    private static function normalize(name : String) : String
    { return name.toLowerCase().replace(/[^a-z:]/g, "") }

    private static function reportUnknownParameter(name : String) : void
    { debug("Error: Unknown parameter: " + name) }

    // -----------------------------------------------------

    private function getString(name : String, fallback : String) : String
    { return name in parameters ? parameters[name] : fallback }

    private function getBoolean
      (name : String, fallback : Boolean) : Boolean
    {
      if (name in parameters)
        return $getBoolean(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBoolean
      (name : String, value : String, fallback : Boolean) : Boolean
    {
      try
        { return $$getBoolean(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, ["true", "false"])

          return fallback
        }

      throw new Error
    }

    private function $$getBoolean(value : String) : Boolean
    {
      if (value == "true")
        return true
      else if (value == "false")
        return false
      else
        throw new Error
    }

    // -----------------------------------------------------

    private function getBitratePolicy
      (name : String, fallback : BitratePolicy) : BitratePolicy
    {
      if (name in parameters)
        return $getBitratePolicy(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBitratePolicy
      (name : String, value : String,
       fallback : BitratePolicy) : BitratePolicy
    {
      try
        { return $$getBitratePolicy(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, BITRATE_POLICY_VALUES)

          return fallback
        }

      throw new Error
    }

    private const BITRATE_POLICY_VALUES : Array =
      ["<number>kbps", "min", "max", "best"]

    private function $$getBitratePolicy(value : String) : BitratePolicy
    {
      if (value == "max")
        return BitratePolicy.MAX
      else if (value == "min")
        return BitratePolicy.MIN
      else if (value == "best")
        return BitratePolicy.BEST
      else if (Bitrate.parse(value))
        return BitratePolicy.specific(Bitrate.parse(value))
      else
        throw new Error
    }

    // -----------------------------------------------------

    private function reportInvalidParameter
      (name : String, value : String, validValues : Array) : void
    {
      debug("Error: Invalid parameter: " +
            "“" + originalParameterNames[name] + "=" + value + "”; " +
            getInvalidParameterHint(validValues) + ".")
    }

    private function getInvalidParameterHint(values : Array) : String
    { return $getInvalidParameterHint(getQuotedValues(values)) }

    private function $getInvalidParameterHint(values : Array) : String
    {
      return "please use " +
        "either " + values.slice(0, -1).join(", ") + " " +
        "or " + values[values.length - 1]
    }

    private function getQuotedValues(values : Array) : Array
    {
      var result : Array = []

      for each (var value : String in values)
        result.push("“" + value + "”")

      return result
    }
  }
}

package goplayer
{
  public class ConfigurationParser
  {
    public static const DEFAULT_API_URL : String
      = "http://staging.streamio.com/api"
    public static const DEFAULT_SKIN_URL : String = "goplayer-skin.swf"
    public static const DEFAULT_TRACKER_ID : String = "global"

    public static const VALID_PARAMETERS : Array = [
      "api", "tracker", "skin", "video", "bitrate",
      "enablertmp", "autoplay", "loop",
      "skin:showchrome", "skin:showtitle" ]

    private const result : Configuration = new Configuration

    private var parameters : Object
    private var originalParameterNames : Object

    public function ConfigurationParser
      (parameters : Object, originalParameterNames : Object)
    {
      this.parameters = parameters
      this.originalParameterNames = originalParameterNames
    }

    public function execute() : void
    {
      result.apiURL = getString("api", DEFAULT_API_URL)
      result.trackerID = getString("tracker", DEFAULT_TRACKER_ID)
      result.skinURL = getString("skin", DEFAULT_SKIN_URL)
      result.movieID = getString("video", null)
      result.bitratePolicy = getBitratePolicy("bitrate", BitratePolicy.BEST)
      result.enableRTMP = getBoolean("enablertmp", true)
      result.enableAutoplay = getBoolean("autoplay", false)
      result.enableLooping = getBoolean("loop", false)
      result.enableChrome = getBoolean("skin:showchrome", true)
      result.enableTitle = getBoolean("skin:showtitle", true)
    }

    public static function parse(parameters : Object) : Configuration
    {
      const normalizedParameters : Object = {}
      const originalParameterNames : Object = {}

      for (var name : String in parameters)
        if (VALID_PARAMETERS.indexOf(normalize(name)) == -1)
          reportUnknownParameter(name)
        else
          normalizedParameters[normalize(name)] = parameters[name],
            originalParameterNames[normalize(name)] = name

      const parser : ConfigurationParser = new ConfigurationParser
        (normalizedParameters, originalParameterNames)

      parser.execute()

      return parser.result
    }

    private static function normalize(name : String) : String
    { return name.toLowerCase().replace(/[^a-z:]/g, "") }

    private static function reportUnknownParameter(name : String) : void
    { debug("Error: Unknown parameter: " + name) }

    // -----------------------------------------------------

    private function getString(name : String, fallback : String) : String
    { return name in parameters ? parameters[name] : fallback }

    private function getBoolean
      (name : String, fallback : Boolean) : Boolean
    {
      if (name in parameters)
        return $getBoolean(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBoolean
      (name : String, value : String, fallback : Boolean) : Boolean
    {
      try
        { return $$getBoolean(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, ["true", "false"])

          return fallback
        }

      throw new Error
    }

    private function $$getBoolean(value : String) : Boolean
    {
      if (value == "true")
        return true
      else if (value == "false")
        return false
      else
        throw new Error
    }

    // -----------------------------------------------------

    private function getBitratePolicy
      (name : String, fallback : BitratePolicy) : BitratePolicy
    {
      if (name in parameters)
        return $getBitratePolicy(name, parameters[name], fallback)
      else
        return fallback
    }

    private function $getBitratePolicy
      (name : String, value : String,
       fallback : BitratePolicy) : BitratePolicy
    {
      try
        { return $$getBitratePolicy(value) }
      catch (error : Error)
        {
          reportInvalidParameter(name, value, BITRATE_POLICY_VALUES)

          return fallback
        }

      throw new Error
    }

    private const BITRATE_POLICY_VALUES : Array =
      ["<number>kbps", "min", "max", "best"]

    private function $$getBitratePolicy(value : String) : BitratePolicy
    {
      if (value == "max")
        return BitratePolicy.MAX
      else if (value == "min")
        return BitratePolicy.MIN
      else if (value == "best")
        return BitratePolicy.BEST
      else if (Bitrate.parse(value))
        return BitratePolicy.specific(Bitrate.parse(value))
      else
        throw new Error
    }

    // -----------------------------------------------------

    private function reportInvalidParameter
      (name : String, value : String, validValues : Array) : void
    {
      debug("Error: Invalid parameter: " +
            "“" + originalParameterNames[name] + "=" + value + "”; " +
            getInvalidParameterHint(validValues) + ".")
    }

    private function getInvalidParameterHint(values : Array) : String
    { return $getInvalidParameterHint(getQuotedValues(values)) }

    private function $getInvalidParameterHint(values : Array) : String
    {
      return "please use " +
        "either " + values.slice(0, -1).join(", ") + " " +
        "or " + values[values.length - 1]
    }

    private function getQuotedValues(values : Array) : Array
    {
      var result : Array = []

      for each (var value : String in values)
        result.push("“" + value + "”")

      return result
    }
  }
}

Change default Streamio API URL to use .com instead of .se.


package org.nuxeo.ecm.flex.dto
{
  import mx.collections.ArrayCollection;
  import flash.utils.IExternalizable;
  import flash.utils.IDataInput;
  import flash.utils.IDataOutput;
  import mx.core.IUID;

  [RemoteClass(alias="org.nuxeo.ecm.flex.javadto.FlexDocumentModel")]
  public class FlexDocumentModel implements IExternalizable, IUID

  {
    private var _docRef:String;
    private var _name:String;
    private var _path:String;
    private var _lifeCycleState:String;
    private var _data:Object;
    private var _dirty:Object;
    private var _type:String;
  private var _isFolder:Boolean;

    public function FlexDocumentModel()
    {
      _name="init_from_flex";
    }

    public function setup(type:String,name:String,parentPath:String):void
    {
      _type=type;
      _name=name;
      _path=parentPath+_name;
      _dirty = new Object();
      _data = new Object();
    }

    public function get uid(): String
    {
            return _docRef;
    }

    public function set uid(uid:String): void
    {

    }

    public function readExternal(input:IDataInput):void {
            _docRef = input.readUTF();
      _name = input.readUTF();
      _path = input.readUTF();
      _lifeCycleState = input.readUTF();
      _type=input.readUTF();
      _isFolder=input.readBoolean();
      _data = input.readObject();
      _dirty = new Object();
        }

    public function writeExternal(output:IDataOutput):void {
            output.writeUTF(_docRef);
            output.writeUTF(_name);
            output.writeUTF(_path);
            output.writeUTF(_lifeCycleState);
            output.writeUTF(_type);
      output.writeObject(_dirty);
            //output.writeObject(_data);
        }

    public function get id():String
    {
      return _docRef;
    }

    public function get name():String
    {
      return _name;
    }

    public function get contentdata():Object
    {
      return _data;
    }

    public function set name(value:String):void
    {
      _name= value;
    }

    public function getTitle():String
    {
      //return "fakeTitle";
      return _data.dublincore.title;
    }

    public function setTitle(value:String):void
    {
      _data.dublincore.title=value;
      _dirty.dublincore_title=value;
    }

    public function getProperty(schemaName:String, fieldName:String):String
    {
      return _data[schemaName][fieldName];
    }

    public function setProperty(schemaName:String, fieldName:String, value:String):void
    {
      _data[schemaName][fieldName]=value;
      _dirty[schemaName+":"+fieldName]=value;
    }

    public function isFolder():Boolean
    {
      return _isFolder;
    }

  }

}

package org.nuxeo.ecm.flex.dto
{
  import mx.collections.ArrayCollection;
  import flash.utils.IExternalizable;
  import flash.utils.IDataInput;
  import flash.utils.IDataOutput;
  import mx.core.IUID;

  [RemoteClass(alias="org.nuxeo.ecm.flex.javadto.FlexDocumentModel")]
  public class FlexDocumentModel implements IExternalizable, IUID

  {
    private var _docRef:String;
    private var _name:String;
    private var _path:String;
    private var _lifeCycleState:String;
    private var _data:Object;
    private var _dirty:Object;
    private var _type:String;
  private var _isFolder:Boolean;

    public function FlexDocumentModel()
    {
      _name="init_from_flex";
    }

    public function setup(type:String,name:String,parentPath:String):void
    {
      _type=type;
      _name=name;
      _path=parentPath+_name;
      _dirty = new Object();
      _data = new Object();
    }

    public function get uid(): String
    {
            return _docRef;
    }

    public function set uid(uid:String): void
    {

    }

    public function readExternal(input:IDataInput):void {
            _docRef = input.readUTF();
      _name = input.readUTF();
      _path = input.readUTF();
      _lifeCycleState = input.readUTF();
      _type=input.readUTF();
      _isFolder=input.readBoolean();
      _data = input.readObject();
      _dirty = new Object();
        }

    public function writeExternal(output:IDataOutput):void {
            output.writeUTF(_docRef);
            output.writeUTF(_name);
            output.writeUTF(_path);
            output.writeUTF(_lifeCycleState);
            output.writeUTF(_type);
      output.writeObject(_dirty);
            //output.writeObject(_data);
        }

    public function get id():String
    {
      return _docRef;
    }

    public function get name():String
    {
      return _name;
    }

    public function get doctype():String
    {
      return _type;
    }

    public function get contentdata():Object
    {
      return _data;
    }

    public function set name(value:String):void
    {
      _name= value;
    }

    public function getTitle():String
    {
      //return "fakeTitle";
      return _data.dublincore.title;
    }

    public function setTitle(value:String):void
    {
      _data.dublincore.title=value;
      _dirty.dublincore_title=value;
    }

    public function getProperty(schemaName:String, fieldName:String):String
    {
      return _data[schemaName][fieldName];
    }

    public function setProperty(schemaName:String, fieldName:String, value:String):void
    {
      _data[schemaName][fieldName]=value;
      _dirty[schemaName+":"+fieldName]=value;
    }

    public function isFolder():Boolean
    {
      return _isFolder;
    }

    public function getSchemas():Array
    {
      var schemas:Array = new Array();
      var schemaName:Object;
      for (schemaName in _data)
      {
      schemas.push(schemaName);
      }
      return schemas;
    }

    public function getSchema(schemaName:String):Object
    {
      return _data[schemaName];
    }

    public function getFieldNames(schemaName:String):Array
    {
      var fieldNames:Array = new Array();
      var fieldName:Object;
      for (fieldName in _data[schemaName])
      {
      fieldNames.push(fieldName);
      }
      return fieldNames;
    }

  }

}

Add introspection to FlexDocumentModel

Add introspection to FlexDocumentModel


////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package
{
	import flash.events.*;
	import mx.utils.*;

	[Event(name="valueExpression", type="RunCodeEvent")]
	public class ConditionalValue extends EventDispatcher
	{
		// Asserts such as AssertPropertyValue use value=...
		public var value:Object = null;

		// CompareBitmap uses url=...
		public var url:String = null;

		/**
		* These are possibilities for the environment.
		* Use Inspectable so that test authoring is a little easier.
		* For details: https://zerowing.corp.adobe.com/display/flexmobile/Multiple+Device%2C+DPI%2C+OS+Support
		**/

		[Inspectable(enumeration="win,mac,android,iphone,ios,qnx")]
		public var os:String = null;

		[Inspectable(enumeration="android22,android23,android234,android31,iphone421,iphone50,ios4,ios5,ios6")]
		public var osVersion:String = null;

		/**
		 * The targetOS is either null or set in the UnitTester's cv as the value to match
		 * against the os properties from <ConditionalValue> elements present in the test
		 * cases. See MultiResult.chooseCV()
		 */
		[Inspectable(enumeration="android,ios")]
		public var targetOS:String = null;

		// General, "marketing number" pixel density
		[Inspectable(enumeration="160,240,320,480")]
		public var deviceDensity:Number = -1;

		// Exact pixel density reported by AIR's Capabilities.screenDPI
		public var screenDPI:Number = -1;

		// Exact
		public var deviceWidth:Number = -1;

		// Exact
		public var deviceHeight:Number = -1;

		[Inspectable(enumeration="16,32")]
		public var color:Number = -1;

		[Inspectable(enumeration="air,desire,droid,droid2,droidPro,droidX,evo,incredible,iPad,iPad2,iPodTouch3GS,iPodTouch4G,nexusOne,playbook,xoom")]
		public var device:String = null;

		/**
		* These are used to make file name legible to humans and allow
		* parsing of them.
		**/
		public static const SCREENDPI_SUFFIX:String = "scrDPI";
		public static const DENSITY_SUFFIX:String = "ppi";
		public static const WIDTH_SUFFIX:String = "w";
		public static const HEIGHT_SUFFIX:String = "h";
		public static const COLOR_SUFFIX:String = "bit";
		public static const PNG_SUFFIX:String = ".png";
		public static const DELIMITER1:String = "@"; // between testID and settings
		public static const DELIMITER2:String = "_"; // between settings

		/**
		* Constructor
		**/
		public function ConditionalValue(){}

		/**
		* Returns true if all items are at default values (e.g. the default, catch-all CV).
		**/
		public function isDefault():Boolean{
			return ((os == null) &&
					(osVersion == null) &&
					(screenDPI == -1) &&
					(deviceDensity == -1) &&
					(deviceWidth == -1) &&
					(deviceHeight == -1) &&
					(color == -1) &&
					(device == null) &&
					(targetOS == null)
				);
		}

		/**
		* Uses the properties which are set to create a file name for a baseline image.
		**/
		public function createFilename(testID:String):String{
			var ret:String = null;
			var consistent:Boolean = false;
			var testCV:ConditionalValue = new ConditionalValue();

			ret = testID + DELIMITER1;

			if( os != null ) {
				ret += os + DELIMITER2;
			}

/*			if( targetOS != null ) {
				ret += targetOS + DELIMITER2;
			}*/

			if( osVersion != null ){
				ret += osVersion + DELIMITER2;
			}

			if( screenDPI > -1 ){
				ret += screenDPI.toString() + SCREENDPI_SUFFIX + DELIMITER2;
			}

			if( deviceDensity > -1 ){
				ret += deviceDensity.toString() + DENSITY_SUFFIX + DELIMITER2;
			}

			if( deviceWidth > -1 ){
				ret += deviceWidth.toString() + WIDTH_SUFFIX + DELIMITER2;
			}

			if( deviceHeight > -1 ){
				ret += deviceHeight.toString() + HEIGHT_SUFFIX + DELIMITER2;
			}

			if( color > -1 ){
				ret += color.toString() + COLOR_SUFFIX + DELIMITER2;
			}

			if( device != null ){
				ret += device;
			}else{
				// Remove last DELIMITER2.
				if( ret.lastIndexOf(DELIMITER2) == ret.length - 1 ){
					ret = ret.substr(0, ret.length - 1);
				}
			}

			ret += PNG_SUFFIX;

			trace("ConditionalValue ret="+ret+"; screenDPI="+screenDPI+"; density="+deviceDensity);

			// Be sure we'll be able to parse what we wrote when we read it later.
			if( testCV.parseFilename( ret ) ){
				consistent = (testCV.os == os &&
							 testCV.osVersion == osVersion &&
							 testCV.screenDPI == screenDPI &&
							 testCV.deviceDensity == deviceDensity &&
							 testCV.deviceWidth == deviceWidth &&
							 testCV.deviceHeight == deviceHeight &&
							 testCV.color == color &&
							 testCV.device == device );
			}

			if( consistent ){
				return ret;
			}else{
				trace("ConditionalValue inconsistency:");
				trace("\twhat\ttestCV\tactualCV");
				trace("\tos\t" + testCV.os + "\t" + os);
				trace("\tosVersion\t" + testCV.osVersion + "\t" + osVersion);
				trace("\tscreenDPI\t" + testCV.screenDPI + "\t" + screenDPI);
				trace("\tdeviceDensity\t" + testCV.deviceDensity + "\t" + deviceDensity);
				trace("\tdeviceWidth\t" + testCV.deviceWidth + "\t" + deviceWidth);
				trace("\tdeviceHeight\t" + testCV.deviceHeight + "\t" + deviceHeight);
				trace("\tcolor\t" + testCV.color + "\t" + color);
				trace("\tdevice\t" + testCV.device + "\t" + device);

				return null;
			}
		}

		/**
		* Populate values from a filename.
		**/
		public function parseFilename(filename:String):Boolean{
			var tokens:Array = null;
			var curToken:String = null;
			var tokenDone:Boolean = false;
			var i:int = 0;
			var j:int = 0;

			if( filename != null ){
				// Remove the extension.
				if( filename.indexOf( PNG_SUFFIX ) > -1 ){
					filename = filename.substring( 0, filename.indexOf( PNG_SUFFIX ) );
				}

				if( (filename != null) && (StringUtil.trim( filename ) != "") ){
					tokens = filename.split( DELIMITER1 );
				}

				// tokens[0] is the test case, and tokens[1] is the data.
				tokens = tokens[1].split( DELIMITER2 );

				if( (tokens != null) && (tokens.length > 0) ){
					for( i = 0; i < tokens.length; ++i ){
						curToken = tokens[ i ];
						tokenDone = false;

						// Look for os.
						for( j = 0; j < DeviceNames.OS_VALUES.length; ++j ){
							if( curToken == DeviceNames.OS_VALUES[ j ] ){
								os = curToken;
								targetOS = curToken;
								tokenDone = true;
								break;
							}
						}

						if( !tokenDone ){
							// Look for os version.
							for( j = 0; j < DeviceNames.OS_VERSION_VALUES.length; ++j ){
								if( curToken == DeviceNames.OS_VERSION_VALUES[ j ] ){
									osVersion = curToken;
									tokenDone = true;
									break;
								}
							}
						}

						if( !tokenDone ){
							// Look for screenDPI
							if( curToken.indexOf( SCREENDPI_SUFFIX ) > -1 ){
								curToken = curToken.substring( 0, curToken.indexOf( SCREENDPI_SUFFIX ) );
								if( (curToken != null) && (StringUtil.trim( curToken ) != "") ){
									screenDPI = new Number( curToken );
									tokenDone = true;
								}
							}
						}

						if( !tokenDone ){
							// Look for density.
							if( curToken.indexOf( DENSITY_SUFFIX ) > -1 ){
								curToken = curToken.substring( 0, curToken.indexOf( DENSITY_SUFFIX ) );
								if( (curToken != null) && (StringUtil.trim( curToken ) != "") ){
									deviceDensity = new Number( curToken );
									tokenDone = true;
								}
							}
						}

						if( !tokenDone ){
							// Look for width.
							if( curToken.indexOf( WIDTH_SUFFIX ) > -1 ){
								curToken = curToken.substring( 0, curToken.indexOf( WIDTH_SUFFIX ) );
								if( (curToken != null) && (StringUtil.trim( curToken ) != "") ){
									deviceWidth = new Number( curToken );
									tokenDone = true;
								}
							}
						}

						if( !tokenDone ){
							// Look for height.
							if( curToken.indexOf( HEIGHT_SUFFIX ) > -1 ){
								curToken = curToken.substring( 0, curToken.indexOf( HEIGHT_SUFFIX ) );
								if( (curToken != null) && (StringUtil.trim( curToken ) != "") ){
									deviceHeight = new Number( curToken );
									tokenDone = true;
								}
							}
						}

						if( !tokenDone ){
							// Look for color.
							if( curToken.indexOf( COLOR_SUFFIX ) > -1 ){
								curToken = curToken.substring( 0, curToken.indexOf( COLOR_SUFFIX ) );
								if( (curToken != null) && (StringUtil.trim( curToken ) != "") ){
									color = new Number( curToken );
									tokenDone = true;
								}
							}
						}

						if( !tokenDone ){
							// Look for device.
							for( j = 0; j < DeviceNames.DEVICE_VALUES.length; ++j ){
								if( curToken == DeviceNames.DEVICE_VALUES[ j ] ){
									device = curToken;
									tokenDone = true;
									break;
								}
							}
						}

						if( !tokenDone ){
							trace("trouble with token: " + curToken);
						}
					}
				}
			}

			// If anything went wrong, tokenDone will be false.
			return tokenDone;
		}

		/**
		* Return a list of the properties.
		**/
		override public function toString():String{
			var ret:String;

			ret = "\tvalue=" + String(value);
			ret += "\n\turl=" + url;
			ret += "\n\tos=" + os;
			ret += "\n\ttargetOS=" + targetOS;
			ret += "\n\tosVersion=" + osVersion;
			ret += "\n\tscreenDPI=" + screenDPI;
			ret += "\n\tdeviceDensity=" + deviceDensity;
			ret += "\n\tdeviceWidth=" + deviceWidth;
			ret += "\n\tdeviceHeight=" + deviceHeight;
			ret += "\n\tcolor=" + color;
			ret += "\n\tdevice=" + device;

			return ret;
		}

	}
}

////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
package
{
	import flash.events.*;
	import mx.utils.*;

	[Event(name="valueExpression", type="RunCodeEvent")]
	public class ConditionalValue extends EventDispatcher
	{
		// Asserts such as AssertPropertyValue use value=...
		public var value:Object = null;

		// CompareBitmap uses url=...
		public var url:String = null;

		/**
		* These are possibilities for the environment.
		* Use Inspectable so that test authoring is a little easier.
		* For details: https://zerowing.corp.adobe.com/display/flexmobile/Multiple+Device%2C+DPI%2C+OS+Support
		**/

		[Inspectable(enumeration="win,mac,android,iphone,ios,qnx")]
		public var os:String = null;

		[Inspectable(enumeration="android22,android23,android234,android31,iphone421,iphone50,ios4,ios5,ios6")]
		public var osVersion:String = null;

		/**
		 * The targetOS is either null or set in the UnitTester's cv as the value to match
		 * against the os properties from <ConditionalValue> elements present in the test
		 * cases. See MultiResult.chooseCV()
		 */
		[Inspectable(enumeration="android,ios")]
		public var targetOS:String = null;

		// General, "marketing number" pixel density
		[Inspectable(enumeration="120,160,240,320,480,640")]
		public var deviceDensity:Number = -1;

		// Exact pixel density reported by AIR's Capabilities.screenDPI
		public var screenDPI:Number = -1;

		// Exact
		public var deviceWidth:Number = -1;

		// Exact
		public var deviceHeight:Number = -1;

		[Inspectable(enumeration="16,32")]
		public var color:Number = -1;

		[Inspectable(enumeration="air,desire,droid,droid2,droidPro,droidX,evo,incredible,iPad,iPad2,iPodTouch3GS,iPodTouch4G,nexusOne,playbook,xoom")]
		public var device:String = null;

		/**
		* These are used to make file name legible to humans and allow
		* parsing of them.
		**/
		public static const SCREENDPI_SUFFIX:String = "scrDPI";
		public static const DENSITY_SUFFIX:String = "ppi";
		public static const WIDTH_SUFFIX:String = "w";
		public static const HEIGHT_SUFFIX:String = "h";
		public static const COLOR_SUFFIX:String = "bit";
		public static const PNG_SUFFIX:String = ".png";
		public static const DELIMITER1:String = "@"; // between testID and settings
		public static const DELIMITER2:String = "_"; // between settings

		/**
		* Constructor
		**/
		public function ConditionalValue(){}

		/**
		* Returns true if all items are at default values (e.g. the default, catch-all CV).
		**/
		public function isDefault():Boolean{
			return ((os == null) &&
					(osVersion == null) &&
					(screenDPI == -1) &&
					(deviceDensity == -1) &&
					(deviceWidth == -1) &&
					(deviceHeight == -1) &&
					(color == -1) &&
					(device == null) &&
					(targetOS == null)
				);
		}

		/**
		* Uses the properties which are set to create a file name for a baseline image.
		**/
		public function createFilename(testID:String):String{
			var ret:String = null;
			var consistent:Boolean = false;
			var testCV:ConditionalValue = new ConditionalValue();

			ret = testID + DELIMITER1;

			if( os != null ) {
				ret += os + DELIMITER2;
			}

/*			if( targetOS != null ) {
				ret += targetOS + DELIMITER2;
			}*/

			if( osVersion != null ){
				ret += osVersion + DELIMITER2;
			}

			if( screenDPI > -1 ){
				ret += screenDPI.toString() + SCREENDPI_SUFFIX + DELIMITER2;
			}

			if( deviceDensity > -1 ){
				ret += deviceDensity.toString() + DENSITY_SUFFIX + DELIMITER2;
			}

			if( deviceWidth > -1 ){
				ret += deviceWidth.toString() + WIDTH_SUFFIX + DELIMITER2;
			}

			if( deviceHeight > -1 ){
				ret += deviceHeight.toString() + HEIGHT_SUFFIX + DELIMITER2;
			}

			if( color > -1 ){
				ret += color.toString() + COLOR_SUFFIX + DELIMITER2;
			}

			if( device != null ){
				ret += device;
			}else{
				// Remove last DELIMITER2.
				if( ret.lastIndexOf(DELIMITER2) == ret.length - 1 ){
					ret = ret.substr(0, ret.length - 1);
				}
			}

			ret += PNG_SUFFIX;

			trace("ConditionalValue ret="+ret+"; screenDPI="+screenDPI+"; density="+deviceDensity);

			// Be sure we'll be able to parse what we wrote when we read it later.
			if( testCV.parseFilename( ret ) ){
				consistent = (testCV.os == os &&
							 testCV.osVersion == osVersion &&
							 testCV.screenDPI == screenDPI &&
							 testCV.deviceDensity == deviceDensity &&
							 testCV.deviceWidth == deviceWidth &&
							 testCV.deviceHeight == deviceHeight &&
							 testCV.color == color &&
							 testCV.device == device );
			}

			if( consistent ){
				return ret;
			}else{
				trace("ConditionalValue inconsistency:");
				trace("\twhat\ttestCV\tactualCV");
				trace("\tos\t" + testCV.os + "\t" + os);
				trace("\tosVersion\t" + testCV.osVersion + "\t" + osVersion);
				trace("\tscreenDPI\t" + testCV.screenDPI + "\t" + screenDPI);
				trace("\tdeviceDensity\t" + testCV.deviceDensity + "\t" + deviceDensity);
				trace("\tdeviceWidth\t" + testCV.deviceWidth + "\t" + deviceWidth);
				trace("\tdeviceHeight\t" + testCV.deviceHeight + "\t" + deviceHeight);
				trace("\tcolor\t" + testCV.color + "\t" + color);
				trace("\tdevice\t" + testCV.device + "\t" + device);

				return null;
			}
		}

		/**
		* Populate values from a filename.
		**/
		public function parseFilename(filename:String):Boolean{
			var tokens:Array = null;
			var curToken:String = null;
			var tokenDone:Boolean = false;
			var i:int = 0;
			var j:int = 0;

			if( filename != null ){
				// Remove the extension.
				if( filename.indexOf( PNG_SUFFIX ) > -1 ){
					filename = filename.substring( 0, filename.indexOf( PNG_SUFFIX ) );
				}

				if( (filename != null) && (StringUtil.trim( filename ) != "") ){
					tokens = filename.split( DELIMITER1 );
				}

				// tokens[0] is the test case, and tokens[1] is the data.
				tokens = tokens[1].split( DELIMITER2 );

				if( (tokens != null) && (tokens.length > 0) ){
					for( i = 0; i < tokens.length; ++i ){
						curToken = tokens[ i ];
						tokenDone = false;

						// Look for os.
						for( j = 0; j < DeviceNames.OS_VALUES.length; ++j ){
							if( curToken == DeviceNames.OS_VALUES[ j ] ){
								os = curToken;
								targetOS = curToken;
								tokenDone = true;
								break;
							}
						}

						if( !tokenDone ){
							// Look for os version.
							for( j = 0; j < DeviceNames.OS_VERSION_VALUES.length; ++j ){
								if( curToken == DeviceNames.OS_VERSION_VALUES[ j ] ){
									osVersion = curToken;
									tokenDone = true;
									break;
								}
							}
						}

						if( !tokenDone ){
							// Look for screenDPI
							if( curToken.indexOf( SCREENDPI_SUFFIX ) > -1 ){
								curToken = curToken.substring( 0, curToken.indexOf( SCREENDPI_SUFFIX ) );
								if( (curToken != null) && (StringUtil.trim( curToken ) != "") ){
									screenDPI = new Number( curToken );
									tokenDone = true;
								}
							}
						}

						if( !tokenDone ){
							// Look for density.
							if( curToken.indexOf( DENSITY_SUFFIX ) > -1 ){
								curToken = curToken.substring( 0, curToken.indexOf( DENSITY_SUFFIX ) );
								if( (curToken != null) && (StringUtil.trim( curToken ) != "") ){
									deviceDensity = new Number( curToken );
									tokenDone = true;
								}
							}
						}

						if( !tokenDone ){
							// Look for width.
							if( curToken.indexOf( WIDTH_SUFFIX ) > -1 ){
								curToken = curToken.substring( 0, curToken.indexOf( WIDTH_SUFFIX ) );
								if( (curToken != null) && (StringUtil.trim( curToken ) != "") ){
									deviceWidth = new Number( curToken );
									tokenDone = true;
								}
							}
						}

						if( !tokenDone ){
							// Look for height.
							if( curToken.indexOf( HEIGHT_SUFFIX ) > -1 ){
								curToken = curToken.substring( 0, curToken.indexOf( HEIGHT_SUFFIX ) );
								if( (curToken != null) && (StringUtil.trim( curToken ) != "") ){
									deviceHeight = new Number( curToken );
									tokenDone = true;
								}
							}
						}

						if( !tokenDone ){
							// Look for color.
							if( curToken.indexOf( COLOR_SUFFIX ) > -1 ){
								curToken = curToken.substring( 0, curToken.indexOf( COLOR_SUFFIX ) );
								if( (curToken != null) && (StringUtil.trim( curToken ) != "") ){
									color = new Number( curToken );
									tokenDone = true;
								}
							}
						}

						if( !tokenDone ){
							// Look for device.
							for( j = 0; j < DeviceNames.DEVICE_VALUES.length; ++j ){
								if( curToken == DeviceNames.DEVICE_VALUES[ j ] ){
									device = curToken;
									tokenDone = true;
									break;
								}
							}
						}

						if( !tokenDone ){
							trace("trouble with token: " + curToken);
						}
					}
				}
			}

			// If anything went wrong, tokenDone will be false.
			return tokenDone;
		}

		/**
		* Return a list of the properties.
		**/
		override public function toString():String{
			var ret:String;

			ret = "\tvalue=" + String(value);
			ret += "\n\turl=" + url;
			ret += "\n\tos=" + os;
			ret += "\n\ttargetOS=" + targetOS;
			ret += "\n\tosVersion=" + osVersion;
			ret += "\n\tscreenDPI=" + screenDPI;
			ret += "\n\tdeviceDensity=" + deviceDensity;
			ret += "\n\tdeviceWidth=" + deviceWidth;
			ret += "\n\tdeviceHeight=" + deviceHeight;
			ret += "\n\tcolor=" + color;
			ret += "\n\tdevice=" + device;

			return ret;
		}

	}
}

support 120 and 640 dpi in mustella tests


/******************************************************************************
 * LzRemote.as
 *****************************************************************************/

//* A_LZ_COPYRIGHT_BEGIN ******************************************************
//* Copyright 2001-2008 Laszlo Systems, Inc.  All Rights Reserved.            *
//* Use is subject to license terms.                                          *
//* A_LZ_COPYRIGHT_END ********************************************************

// This file implements the remote debugger protocol.
// When the app starts up, if the query arg remotedebug (global.remotedebug)
// is defined, it is treated as a TCP port number and a remote debug
// socket is opened to that port.

Debug.seqnum = 0;
Debug.inEvalRequest = false;

/**
  * @access private
  */
Debug.sockOpen = function (port) {
  var url = lz.Browser.getLoadURLAsLzURL();
  // Security requires us to talk back to the server we were loaded from
  var host = url.host;
  this.xsock = new XMLSocket();
  this.xsock.onClose = this.brokensocket;
  this.xsock.onXML = this.socketXMLAvailable;
  if (! this.xsock.connect(host, port)) {
    Debug.log("remote debugger could not connect to listener " + host + ":" + port);
  }
  this.writeInitMessage();
}

/**
  * @access private
  */
Debug.writeInitMessage = function () {
    var filename = lz.Browser.getLoadURLAsLzURL();
    var myXML = new XML();
    var init = myXML.createElement("init");
    myXML.appendChild(init);
    init.attributes.filename         = filename;
    init.attributes.language         = "LZX";
    init.attributes.protocol_version = "1.0";
    init.attributes.build      = canvas.lpsbuild;
    init.attributes.lpsversion = canvas.lpsversion;
    init.attributes.lpsrelease = canvas.lpsrelease;
    init.attributes.runtime    = canvas.runtime;
    init.attributes.appid      = "0";
    this.xsock.send(myXML);
}

/**
  * @access private
  */
Debug.sockWriteWarning = function (filename, lineNumber, msg){
    var myXML = new XML();
    var warn = myXML.createElement("warning");
    myXML.appendChild(warn);
    warn.attributes.filename = filename;
    warn.attributes.line = lineNumber;
    warn.attributes.msg = msg;
    this.xsock.send(myXML);
}

/**
  * @access private
  */
Debug.sockWriteLog = function (msg) {
    var myXML = new XML();
    var response = myXML.createElement("log");
    myXML.appendChild(response);
    response.attributes.msg = msg;
    this.xsock.send(myXML);
}

/**
  * @access private
  */
Debug.sockWrite = function (s) {
    this.xsock.send(s);
    this.xsock.send("\n");
    return s;
}

/**
  * @access private
  * Writes out an object as XML
  */
Debug.sockWriteAsXML = function (obj, seqnum) {
    var myXML = new XML();
    var response = myXML.createElement("response");
    response.attributes.seq = seqnum;
    myXML.appendChild(response);

    var val = myXML.createElement("value");
    response.appendChild(val);

    var objtype = typeof(obj);
    val.attributes.type = Debug.__typeof(obj);
    val.attributes.value = String(obj);
    if (objtype == "object") {
        var id = Debug.IDForObject(obj);
        if (id != null) {
            val.attributes.id = id;
        }
    }

    for (var attr in obj) {
        var child = obj[attr];
        var prop = myXML.createElement("property");
        prop.attributes.name = attr;
        prop.attributes.type = Debug.__typeof(child);
        prop.attributes.value = String(child);

        var objtype = typeof(child);
        if (objtype == "object") {
            var id = Debug.IDForObject(child);
            if (id != null) {
                prop.attributes.id = id;
            }
        }

        val.appendChild(prop);
    }

    for (var i = 0; i < this.xmlwarnings.length; i++) {
        var w = this.xmlwarnings[i];
        var filename = w[0];
        var line = w[1];
        var msg = w[2];
        var warn = myXML.createElement("warning");
        warn.attributes.filename = filename;
        warn.attributes.line = line;
        warn.attributes.msg = msg;
        response.appendChild(warn);
    }

    this.xsock.send(myXML);
    Debug.inEvalRequest = false;
    return obj;
}

/**
  * @access private
  */
Debug.sockClose = function () {
 Debug.xsock.close();
}

/**
  * @access private
  * send message to server that socket is gone
  */
Debug.brokensocket = function () {
    Debug.error("socket connection is broken");
}

////////////////////////////////////////////////////////////////
// Redefine the handler for eval warnings, to grab them so we can send them
// back as XML.

/**
  * @access private
  */
Debug.resetWarningHistory = function () {
    Debug.xmlwarnings = [];
}

Debug.resetWarningHistory();

////////////////////////////////////////////////////////////////

/**
  * @access private
  */
Debug.socketXMLAvailable = function (doc) {
    var e = doc.firstChild;
    var rloader = Debug.rdbloader;
    if (e != null) {
        // clear warnings history
        Debug.resetWarningHistory();
        Debug.inEvalRequest = true;
        var seqnum = e.attributes['seq'];
        if (seqnum == null) {
            seqnum = Debug.seqnum++;
        }

        if (e.nodeName == "exec") {
            var expr = e.firstChild.nodeValue;
            rloader.request( { lz_load : false,
                           lzt : "eval",
                           proxied: true,
                           lzrdbseq : seqnum,
                           lz_script : "#file evalString\n#line 0\n" + expr } );

        } else if (e.nodeName == "eval") {
            var expr = e.firstChild.nodeValue;
            rloader.request( {  lz_load : false,
                                lzt : "eval",
                                proxied: true,
                                lzrdbseq : seqnum,
                                lz_script : "#file evalString\n#line 0\n" + expr } );
        } else if (e.nodeName == "inspect") {
            Debug.inEvalRequest = false;
            var id = e.attributes.id;
            Debug.sockWriteAsXML(Debug.ObjectForID(id), seqnum);
        } else {
            Debug.inEvalRequest = false;
            Debug.sockWrite("<response seq='"+seqnum+"'><error msg='unknown remote debug command'>"+e.nodeName+"</error></response>");
        }
    } else {
        Debug.inEvalRequest = false;
        Debug.sockWrite("<response seq='-1'><error msg='null remote debug command'/></response>");
    }
}

/**
  * @access private
  */
Debug.makeRDBLoader = function () {
    this.rdbloader = new LzLoader(canvas, { attachname: 'rdebugloader' });
    this.rdbloader.queuing = true;
}

/**
  * @access private
  * query arg  'remotedebug', if supplied, specifies a TCP port to connect to
  */
Debug.startupRemote = function () {
    if (typeof(remotedebug) != 'undefined') {
        Debug.remoteDebug = true;
        Debug.makeRDBLoader();
        Debug.sockOpen(remotedebug);
    }
}

/******************************************************************************
 * LzRemote.as
 *****************************************************************************/

//* A_LZ_COPYRIGHT_BEGIN ******************************************************
//* Copyright 2001-2008 Laszlo Systems, Inc.  All Rights Reserved.            *
//* Use is subject to license terms.                                          *
//* A_LZ_COPYRIGHT_END ********************************************************

// This file implements the remote debugger protocol.
// When the app starts up, if the query arg remotedebug (global.remotedebug)
// is defined, it is treated as a TCP port number and a remote debug
// socket is opened to that port.

Debug.seqnum = 0;
Debug.inEvalRequest = false;
Debug.remoteDebug = false;

/**
  * @access private
  */
Debug.sockOpen = function (port) {
  var url = lz.Browser.getLoadURLAsLzURL();
  // Security requires us to talk back to the server we were loaded from
  var host = url.host;
  this.xsock = new XMLSocket();
  this.xsock.onClose = this.brokensocket;
  this.xsock.onXML = this.socketXMLAvailable;
  if (! this.xsock.connect(host, port)) {
    Debug.log("remote debugger could not connect to listener " + host + ":" + port);
  }
  this.writeInitMessage();
}

/**
  * @access private
  */
Debug.writeInitMessage = function () {
    var filename = lz.Browser.getLoadURLAsLzURL();
    var myXML = new XML();
    var init = myXML.createElement("init");
    myXML.appendChild(init);
    init.attributes.filename         = filename;
    init.attributes.language         = "LZX";
    init.attributes.protocol_version = "1.0";
    init.attributes.build      = canvas.lpsbuild;
    init.attributes.lpsversion = canvas.lpsversion;
    init.attributes.lpsrelease = canvas.lpsrelease;
    init.attributes.runtime    = canvas.runtime;
    init.attributes.appid      = "0";
    this.xsock.send(myXML);
}

/**
  * @access private
  */
Debug.sockWriteWarning = function (filename, lineNumber, msg){
    var myXML = new XML();
    var warn = myXML.createElement("warning");
    myXML.appendChild(warn);
    warn.attributes.filename = filename;
    warn.attributes.line = lineNumber;
    warn.attributes.msg = msg;
    this.xsock.send(myXML);
}

/**
  * @access private
  */
Debug.sockWriteLog = function (msg) {
    var myXML = new XML();
    var response = myXML.createElement("log");
    myXML.appendChild(response);
    response.attributes.msg = msg;
    this.xsock.send(myXML);
}

/**
  * @access private
  */
Debug.sockWrite = function (s) {
    this.xsock.send(s);
    this.xsock.send("\n");
    return s;
}

/**
  * @access private
  * Writes out an object as XML
  */
Debug.sockWriteAsXML = function (obj, seqnum) {
    var myXML = new XML();
    var response = myXML.createElement("response");
    response.attributes.seq = seqnum;
    myXML.appendChild(response);

    var val = myXML.createElement("value");
    response.appendChild(val);

    var objtype = typeof(obj);
    val.attributes.type = Debug.__typeof(obj);
    val.attributes.value = String(obj);
    if (objtype == "object") {
        var id = Debug.IDForObject(obj);
        if (id != null) {
            val.attributes.id = id;
        }
    }

    for (var attr in obj) {
        var child = obj[attr];
        var prop = myXML.createElement("property");
        prop.attributes.name = attr;
        prop.attributes.type = Debug.__typeof(child);
        prop.attributes.value = String(child);

        var objtype = typeof(child);
        if (objtype == "object") {
            var id = Debug.IDForObject(child);
            if (id != null) {
                prop.attributes.id = id;
            }
        }

        val.appendChild(prop);
    }

    for (var i = 0; i < this.xmlwarnings.length; i++) {
        var w = this.xmlwarnings[i];
        var filename = w[0];
        var line = w[1];
        var msg = w[2];
        var warn = myXML.createElement("warning");
        warn.attributes.filename = filename;
        warn.attributes.line = line;
        warn.attributes.msg = msg;
        response.appendChild(warn);
    }

    this.xsock.send(myXML);
    Debug.inEvalRequest = false;
    return obj;
}

/**
  * @access private
  */
Debug.sockClose = function () {
 Debug.xsock.close();
}

/**
  * @access private
  * send message to server that socket is gone
  */
Debug.brokensocket = function () {
    Debug.error("socket connection is broken");
}

////////////////////////////////////////////////////////////////
// Redefine the handler for eval warnings, to grab them so we can send them
// back as XML.

/**
  * @access private
  */
Debug.resetWarningHistory = function () {
    Debug.xmlwarnings = [];
}

Debug.resetWarningHistory();

////////////////////////////////////////////////////////////////

/**
  * @access private
  */
Debug.socketXMLAvailable = function (doc) {
    var e = doc.firstChild;
    var rloader = Debug.rdbloader;
    if (e != null) {
        // clear warnings history
        Debug.resetWarningHistory();
        Debug.inEvalRequest = true;
        var seqnum = e.attributes['seq'];
        if (seqnum == null) {
            seqnum = Debug.seqnum++;
        }

        if (e.nodeName == "exec") {
            var expr = e.firstChild.nodeValue;
            rloader.request( { lz_load : false,
                           lzt : "eval",
                           proxied: true,
                           lzrdbseq : seqnum,
                           lz_script : "#file evalString\n#line 0\n" + expr } );

        } else if (e.nodeName == "eval") {
            var expr = e.firstChild.nodeValue;
            rloader.request( {  lz_load : false,
                                lzt : "eval",
                                proxied: true,
                                lzrdbseq : seqnum,
                                lz_script : "#file evalString\n#line 0\n" + expr } );
        } else if (e.nodeName == "inspect") {
            Debug.inEvalRequest = false;
            var id = e.attributes.id;
            Debug.sockWriteAsXML(Debug.ObjectForID(id), seqnum);
        } else {
            Debug.inEvalRequest = false;
            Debug.sockWrite("<response seq='"+seqnum+"'><error msg='unknown remote debug command'>"+e.nodeName+"</error></response>");
        }
    } else {
        Debug.inEvalRequest = false;
        Debug.sockWrite("<response seq='-1'><error msg='null remote debug command'/></response>");
    }
}

/**
  * @access private
  */
Debug.makeRDBLoader = function () {
    this.rdbloader = new LzLoader(canvas, { attachname: 'rdebugloader' });
    this.rdbloader.queuing = true;
}

/**
  * @access private
  * query arg  'remotedebug', if supplied, specifies a TCP port to connect to
  */
Debug.startupRemote = function () {
    if (typeof(remotedebug) != 'undefined') {
        Debug.remoteDebug = true;
        Debug.makeRDBLoader();
        Debug.sockOpen(remotedebug);
    }
}

Change 20081030-bargull-zr6 by bargull@dell--p4--2-53 on 2008-10-30 17:35:53
    in /home/Admin/src/svn/openlaszlo/trunk
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: define "remoteDebug"

New Features:

Bugs Fixed: LPP-7145

Technical Reviewer: ptw
QA Reviewer: (pending)
Doc Reviewer: (pending)

Documentation:

Release Notes:

Details:
Define "remoteDebug" to avoid runtime warning

Tests:

git-svn-id: d62bde4b5aa582fdf07c8d403e53e0399a7ed285@11660 fa20e4f9-1d0a-0410-b5f3-dc9c16b8b17c


/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the at.matthew.httpstreaming package.
 *
 * The Initial Developer of the Original Code is
 * Matthew Kaufman.
 * Portions created by the Initial Developer are Copyright (C) 2011
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** */

 package org.denivip.osmf.net.httpstreaming.hls
{
	import flash.net.URLRequest;
	import flash.utils.ByteArray;

	import org.denivip.osmf.elements.m3u8Classes.M3U8Item;
	import org.denivip.osmf.elements.m3u8Classes.M3U8Playlist;
	import org.denivip.osmf.elements.m3u8Classes.M3U8PlaylistParser;
	import org.osmf.events.DVRStreamInfoEvent;
	import org.osmf.events.HTTPStreamingEvent;
	import org.osmf.events.HTTPStreamingIndexHandlerEvent;
	import org.osmf.events.MediaError;
	import org.osmf.events.MediaErrorEvent;
	import org.osmf.events.ParseEvent;
	import org.osmf.logging.Log;
	import org.osmf.logging.Logger;
	import org.osmf.net.httpstreaming.HTTPStreamRequest;
	import org.osmf.net.httpstreaming.HTTPStreamRequestKind;
	import org.osmf.net.httpstreaming.HTTPStreamingIndexHandlerBase;
	import org.osmf.net.httpstreaming.dvr.DVRInfo;
	import org.osmf.net.httpstreaming.flv.FLVTagScriptDataMode;
	import org.osmf.net.httpstreaming.flv.FLVTagScriptDataObject;

	[Event(name="notifyIndexReady", type="org.osmf.events.HTTPStreamingFileIndexHandlerEvent")]
	[Event(name="notifyRates", type="org.osmf.events.HTTPStreamingFileIndexHandlerEvent")]
	[Event(name="notifyTotalDuration", type="org.osmf.events.HTTPStreamingFileIndexHandlerEvent")]
	[Event(name="requestLoadIndex", type="org.osmf.events.HTTPStreamingFileIndexHandlerEvent")]
	[Event(name="notifyError", type="org.osmf.events.HTTPStreamingFileIndexHandlerEvent")]
	[Event(name="DVRStreamInfo", type="org.osmf.events.DVRStreamInfoEvent")]

	/**
	 *
	 */
	public class HTTPStreamingHLSIndexHandler extends HTTPStreamingIndexHandlerBase
	{
		private static const MAX_ERRORS:int = 10;

		private var _indexInfo:HTTPStreamingHLSIndexInfo;
		private var _baseURL:String;
		private var _rateVec:Vector.<HTTPStreamingM3U8IndexRateItem>;
		private var _segment:int;
		private var _absoluteSegment:int;
		private var _quality:int;

		private var _streamNames:Array;
		private var _streamQualityRates:Array;

		// for error handling (if playlist don't update on server)
		private var _prevPlaylist:String;
		private var _matchCounter:int;

		private var _fromDVR:Boolean;

		override public function dvrGetStreamInfo(indexInfo:Object):void{
			_fromDVR = true;
			initialize(indexInfo);
		}

		override public function initialize(indexInfo:Object):void{
			_indexInfo = indexInfo as HTTPStreamingHLSIndexInfo;
			if(_indexInfo == null){
				logger.error("Incorrect indexInfo!");

				dispatchEvent(new HTTPStreamingEvent(HTTPStreamingEvent.INDEX_ERROR));
				return;
			}

			_streamNames = [];
			_streamQualityRates = [];
			_quality = 0;

			_rateVec = _indexInfo.streams;

			// prepare data for multiquality
			var streamsCount:int = _rateVec.length;
			for(var quality:int = 0; quality < streamsCount; quality++){
				var item:HTTPStreamingM3U8IndexRateItem = _rateVec[quality];

				if(item){
					_streamNames[quality] = item.url;
					_streamQualityRates[quality] = item.bw;
				}
			}

			notifyRatesReady();
			notifyIndexReady(_quality);
		}

		override public function dispose():void{
			_indexInfo = null;
			_rateVec = null;

			_streamNames = null;
			_streamQualityRates = null;

			_prevPlaylist = null;
		}

		/*
			used only in live streaming
		*/
		override public function processIndexData(data:*, indexContext:Object):void{
			// refresh index context
			var rateItem:HTTPStreamingM3U8IndexRateItem = HTTPStreamingM3U8IndexRateItem(indexContext)
			if(indexContext){
				if(_absoluteSegment > 0){
					rateItem.clearManifest();
				}
			}

			// get playlist from binary data
			var ba:ByteArray = ByteArray(data);
			var pl_str:String = ba.readUTFBytes(ba.length);
			if(pl_str.localeCompare(_prevPlaylist) == 0)
				++_matchCounter;

			if(_matchCounter == MAX_ERRORS){ // if delivered playlist again not changed then alert!
				var mediaErr:MediaError = new MediaError(0, "Stream is stuck. Playlist on server don't updated!");
				dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, mediaErr));

				logger.error("Stream is stuck. Playlist on server don't updated!");
			}

			_prevPlaylist = pl_str;

			// simple parsing && update rate items
			var parser:M3U8PlaylistParser = new M3U8PlaylistParser();
			parser.addEventListener(ParseEvent.PARSE_COMPLETE, onComplete);
			parser.addEventListener(ParseEvent.PARSE_ERROR, onError);

			parser.parse(pl_str, rateItem.urlBase);

			// service functions
			function onComplete(e:ParseEvent):void{
				parser.removeEventListener(ParseEvent.PARSE_COMPLETE, onComplete);
				parser.removeEventListener(ParseEvent.PARSE_ERROR, onError);

				var pl:M3U8Playlist = M3U8Playlist(e.data);

				updateRateItem(pl, rateItem);

				notifyRatesReady();
				notifyIndexReady(_quality);
			}

			function onError(e:ParseEvent):void{
				parser.removeEventListener(ParseEvent.PARSE_COMPLETE, onComplete);
				parser.removeEventListener(ParseEvent.PARSE_ERROR, onError);

				// maybe add notification?... do it if you want =)
				logger.warn("Parse error! Maybe incorrect playlist");
			}
		}

		override public function getFileForTime(time:Number, quality:int):HTTPStreamRequest{
			_quality = quality;
			var item:HTTPStreamingM3U8IndexRateItem = _rateVec[quality];
			var manifest:Vector.<HTTPStreamingM3U8IndexItem> = item.manifest;

			var len:int = manifest.length;
			var i:int;
			for(i = 0; i < len; i++){
				if(time < manifest[i].startTime)
					break;
			}
			if(i > 0) --i;

			_segment = i;
			_absoluteSegment = item.sequenceNumber + _segment;

			return getNextFile(quality);
		}

		override public function getNextFile(quality:int):HTTPStreamRequest{
			var item:HTTPStreamingM3U8IndexRateItem = _rateVec[quality];
			var manifest:Vector.<HTTPStreamingM3U8IndexItem> = item.manifest;
			var request:HTTPStreamRequest;

			notifyTotalDuration(item.totalTime, quality, item.live);

			if(item.live){
				if(_absoluteSegment == 0 && _segment == 0){ // Initialize live playback
					_absoluteSegment = item.sequenceNumber + _segment;
				}

				if(_absoluteSegment != (item.sequenceNumber + _segment)){ // We re-loaded the live manifest, need to re-normalize the list
					_segment = _absoluteSegment - item.sequenceNumber;
					if(_segment < 0)
					{
						_segment=0;
						_absoluteSegment = item.sequenceNumber;
					}
					_matchCounter = 0; // reset error counter!
				}
				if(_segment >= manifest.length){ // Try to force a reload
					dispatchEvent(new HTTPStreamingIndexHandlerEvent(HTTPStreamingIndexHandlerEvent.REQUEST_LOAD_INDEX, false, false, item.live, 0, _streamNames, _streamQualityRates, new URLRequest(_rateVec[quality].url), _rateVec[quality], false));
					return new HTTPStreamRequest(HTTPStreamRequestKind.LIVE_STALL, null, 1.0);
				}
			}

			if(_segment >= manifest.length) // if playlist ended, then end =)
				return new HTTPStreamRequest(HTTPStreamRequestKind.DONE);
			else{ // load new chunk
				request = new HTTPStreamRequest(HTTPStreamRequestKind.DOWNLOAD, manifest[_segment].url);

				dispatchEvent(new HTTPStreamingEvent(HTTPStreamingEvent.FRAGMENT_DURATION, false, false, manifest[_segment].duration));

				++_segment;
				++_absoluteSegment;
			}

			return request;
		}

		/*
			Private secton
		*/
		private function notifyRatesReady():void{
			dispatchEvent(
				new HTTPStreamingIndexHandlerEvent(
					HTTPStreamingIndexHandlerEvent.RATES_READY,
					false,
					false,
					false,
					0,
					_streamNames,
					_streamQualityRates
				)
			);
		}

		private function notifyIndexReady(quality:int):void{
			var item:HTTPStreamingM3U8IndexRateItem = _rateVec[quality];

			dispatchDVRStreamInfo(item);

			if(!_fromDVR){
				var initialOffset:Number = NaN;
				if(item.live && _indexInfo.dvrInfo == null)
					initialOffset = item.totalTime - ((item.totalTime/item.manifest.length) * 3);

				dispatchEvent(
					new HTTPStreamingIndexHandlerEvent(
						HTTPStreamingIndexHandlerEvent.INDEX_READY,
						false,
						false,
						item.live,
						initialOffset
					)
				);
			}
			_fromDVR = false;
		}

		private function notifyTotalDuration(duration:Number, quality:int, live:Boolean):void{
			var sdo:FLVTagScriptDataObject = new FLVTagScriptDataObject();
			var metaInfo:Object = new Object();
			if(!live)
				metaInfo.duration = duration;
			else
				metaInfo.duration = 0;

			sdo.objects = ["onMetaData", metaInfo];
			dispatchEvent(
				new HTTPStreamingEvent(
					HTTPStreamingEvent.SCRIPT_DATA,
					false,
					false,
					0,
					sdo,
					FLVTagScriptDataMode.IMMEDIATE
				)
			);
		}

		private function dispatchDVRStreamInfo(item:HTTPStreamingM3U8IndexRateItem):void{
			var dvrInfo:DVRInfo = _indexInfo.dvrInfo;
			if(dvrInfo == null) // Nothing todo here!
				return;

			dvrInfo.isRecording = item.live; // it's simple if not live then VOD =)
			var currentDuration:Number = item.totalTime;
			// make some cheating...
			var currentTime:Number = item.totalTime - ((item.totalTime/item.manifest.length) * 3);

			// update current length of the DVR window
			dvrInfo.curLength = currentTime;// because dvrInfo.startTime is "0.0" =);

			// adjust the start time if we have a DVR rooling window active
			if ((dvrInfo.windowDuration != -1) && (dvrInfo.curLength > dvrInfo.windowDuration))
			{
				dvrInfo.startTime += dvrInfo.curLength - dvrInfo.windowDuration;
				dvrInfo.curLength = dvrInfo.windowDuration;
			}

			dispatchEvent(new DVRStreamInfoEvent(
					DVRStreamInfoEvent.DVRSTREAMINFO,
					false,
					false,
					dvrInfo
				)
			);
		}

		private function updateRateItem(playlist:M3U8Playlist, item:HTTPStreamingM3U8IndexRateItem):void{
			// refresh manifest items
			for each(var m3u8Item:M3U8Item in playlist.streamItems){
				var iItem:HTTPStreamingM3U8IndexItem = new HTTPStreamingM3U8IndexItem(m3u8Item.duration, m3u8Item.url);
				item.addIndexItem(iItem);
			}

			item.setSequenceNumber(playlist.sequenceNumber);
			item.setLive(playlist.isLive);
		}
		protected var logger:Logger = Log.getLogger("org.denivip.osmf.plugins.hls.HTTPStreamingM3U8IndexHandler");
	}
}

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the at.matthew.httpstreaming package.
 *
 * The Initial Developer of the Original Code is
 * Matthew Kaufman.
 * Portions created by the Initial Developer are Copyright (C) 2011
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * ***** END LICENSE BLOCK ***** */

 package org.denivip.osmf.net.httpstreaming.hls
{
	import flash.external.ExternalInterface;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;

	import org.denivip.osmf.elements.m3u8Classes.M3U8Item;
	import org.denivip.osmf.elements.m3u8Classes.M3U8Playlist;
	import org.denivip.osmf.elements.m3u8Classes.M3U8PlaylistParser;
	import org.osmf.events.DVRStreamInfoEvent;
	import org.osmf.events.HTTPStreamingEvent;
	import org.osmf.events.HTTPStreamingIndexHandlerEvent;
	import org.osmf.events.MediaError;
	import org.osmf.events.MediaErrorEvent;
	import org.osmf.events.ParseEvent;
	import org.osmf.logging.Log;
	import org.osmf.logging.Logger;
	import org.osmf.net.httpstreaming.HTTPStreamRequest;
	import org.osmf.net.httpstreaming.HTTPStreamRequestKind;
	import org.osmf.net.httpstreaming.HTTPStreamingIndexHandlerBase;
	import org.osmf.net.httpstreaming.dvr.DVRInfo;
	import org.osmf.net.httpstreaming.flv.FLVTagScriptDataMode;
	import org.osmf.net.httpstreaming.flv.FLVTagScriptDataObject;

	[Event(name="notifyIndexReady", type="org.osmf.events.HTTPStreamingFileIndexHandlerEvent")]
	[Event(name="notifyRates", type="org.osmf.events.HTTPStreamingFileIndexHandlerEvent")]
	[Event(name="notifyTotalDuration", type="org.osmf.events.HTTPStreamingFileIndexHandlerEvent")]
	[Event(name="requestLoadIndex", type="org.osmf.events.HTTPStreamingFileIndexHandlerEvent")]
	[Event(name="notifyError", type="org.osmf.events.HTTPStreamingFileIndexHandlerEvent")]
	[Event(name="DVRStreamInfo", type="org.osmf.events.DVRStreamInfoEvent")]

	/**
	 *
	 */
	public class HTTPStreamingHLSIndexHandler extends HTTPStreamingIndexHandlerBase
	{
		private static const MAX_ERRORS:int = 10;

		private var _indexInfo:HTTPStreamingHLSIndexInfo;
		private var _baseURL:String;
		private var _rateVec:Vector.<HTTPStreamingM3U8IndexRateItem>;
		private var _segment:int;
		private var _absoluteSegment:int;
		private var _quality:int;

		private var _streamNames:Array;
		private var _streamQualityRates:Array;

		// for error handling (if playlist don't update on server)
		private var _prevPlaylist:String;
		private var _matchCounter:int;

		private var _fromDVR:Boolean;

		override public function dvrGetStreamInfo(indexInfo:Object):void{
			_fromDVR = true;
			initialize(indexInfo);
		}

		override public function initialize(indexInfo:Object):void{
			_indexInfo = indexInfo as HTTPStreamingHLSIndexInfo;
			if(_indexInfo == null){
				logger.error("Incorrect indexInfo!");

				dispatchEvent(new HTTPStreamingEvent(HTTPStreamingEvent.INDEX_ERROR));
				return;
			}

			_streamNames = [];
			_streamQualityRates = [];
			_quality = 0;

			_rateVec = _indexInfo.streams;

			// prepare data for multiquality
			var streamsCount:int = _rateVec.length;
			for(var quality:int = 0; quality < streamsCount; quality++){
				var item:HTTPStreamingM3U8IndexRateItem = _rateVec[quality];

				if(item){
					_streamNames[quality] = item.url;
					_streamQualityRates[quality] = item.bw;
				}
			}

			notifyRatesReady();
			notifyIndexReady(_quality);
		}

		override public function dispose():void{
			_indexInfo = null;
			_rateVec = null;

			_streamNames = null;
			_streamQualityRates = null;

			_prevPlaylist = null;
		}

		/*
			used only in live streaming
		*/
		override public function processIndexData(data:*, indexContext:Object):void{
			// refresh index context
			var rateItem:HTTPStreamingM3U8IndexRateItem = HTTPStreamingM3U8IndexRateItem(indexContext)
			if(indexContext){
				if(_absoluteSegment > 0){
					rateItem.clearManifest();
				}
			}

			// get playlist from binary data
			var ba:ByteArray = ByteArray(data);
			var pl_str:String = ba.readUTFBytes(ba.length);
			if(pl_str.localeCompare(_prevPlaylist) == 0)
				++_matchCounter;

			if(_matchCounter == MAX_ERRORS){ // if delivered playlist again not changed then alert!
				var mediaErr:MediaError = new MediaError(0, "Stream is stuck. Playlist on server don't updated!");
				dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, mediaErr));

				logger.error("Stream is stuck. Playlist on server don't updated!");
			}

			_prevPlaylist = pl_str;

			// simple parsing && update rate items
			var parser:M3U8PlaylistParser = new M3U8PlaylistParser();
			parser.addEventListener(ParseEvent.PARSE_COMPLETE, onComplete);
			parser.addEventListener(ParseEvent.PARSE_ERROR, onError);

			parser.parse(pl_str, rateItem.urlBase);

			// service functions
			function onComplete(e:ParseEvent):void{
				parser.removeEventListener(ParseEvent.PARSE_COMPLETE, onComplete);
				parser.removeEventListener(ParseEvent.PARSE_ERROR, onError);

				var pl:M3U8Playlist = M3U8Playlist(e.data);

				updateRateItem(pl, rateItem);

				notifyRatesReady();
				notifyIndexReady(_quality);
			}

			function onError(e:ParseEvent):void{
				parser.removeEventListener(ParseEvent.PARSE_COMPLETE, onComplete);
				parser.removeEventListener(ParseEvent.PARSE_ERROR, onError);

				// maybe add notification?... do it if you want =)
				logger.warn("Parse error! Maybe incorrect playlist");
			}
		}

		override public function getFileForTime(time:Number, quality:int):HTTPStreamRequest{
			_quality = quality;
			var item:HTTPStreamingM3U8IndexRateItem = _rateVec[quality];
			var manifest:Vector.<HTTPStreamingM3U8IndexItem> = item.manifest;

			var len:int = manifest.length;
			var i:int;
			for(i = 0; i < len; i++){
				if(time < manifest[i].startTime)
					break;
			}
			if(i > 0) --i;

			_segment = i;
			_absoluteSegment = item.sequenceNumber + _segment;

			return getNextFile(quality);
		}

		override public function getNextFile(quality:int):HTTPStreamRequest{
			var item:HTTPStreamingM3U8IndexRateItem = _rateVec[quality];
			var manifest:Vector.<HTTPStreamingM3U8IndexItem> = item.manifest;
			var request:HTTPStreamRequest;

			notifyTotalDuration(item.totalTime, quality, item.live);

			if(item.live){
				if(_absoluteSegment == 0 && _segment == 0){ // Initialize live playback
					_absoluteSegment = item.sequenceNumber + _segment;
				}

				if(_absoluteSegment != (item.sequenceNumber + _segment)){ // We re-loaded the live manifest, need to re-normalize the list
					_segment = _absoluteSegment - item.sequenceNumber;
					if(_segment < 0)
					{
						_segment=0;
						_absoluteSegment = item.sequenceNumber;
					}
					_matchCounter = 0; // reset error counter!
				}
				if(_segment >= manifest.length){ // Try to force a reload
					dispatchEvent(new HTTPStreamingIndexHandlerEvent(HTTPStreamingIndexHandlerEvent.REQUEST_LOAD_INDEX, false, false, item.live, 0, _streamNames, _streamQualityRates, new URLRequest(_rateVec[quality].url), _rateVec[quality], false));
					return new HTTPStreamRequest(HTTPStreamRequestKind.LIVE_STALL, null, 1.0);
				}
			}

			if(_segment >= manifest.length){ // if playlist ended, then end =)
				return new HTTPStreamRequest(HTTPStreamRequestKind.DONE);
			}else{ // load new chunk
				request = new HTTPStreamRequest(HTTPStreamRequestKind.DOWNLOAD, manifest[_segment].url);

				dispatchEvent(new HTTPStreamingEvent(HTTPStreamingEvent.FRAGMENT_DURATION, false, false, manifest[_segment].duration));

				++_segment;
				++_absoluteSegment;
			}

			return request;
		}

		/*
			Private secton
		*/
		private function notifyRatesReady():void{
			dispatchEvent(
				new HTTPStreamingIndexHandlerEvent(
					HTTPStreamingIndexHandlerEvent.RATES_READY,
					false,
					false,
					false,
					0,
					_streamNames,
					_streamQualityRates
				)
			);
		}

		private function notifyIndexReady(quality:int):void{
			var item:HTTPStreamingM3U8IndexRateItem = _rateVec[quality];

			dispatchDVRStreamInfo(item);

			if(!_fromDVR){
				var initialOffset:Number = NaN;
				if(item.live && _indexInfo.dvrInfo == null)
					initialOffset = item.totalTime - ((item.totalTime/item.manifest.length) * 3);

				dispatchEvent(
					new HTTPStreamingIndexHandlerEvent(
						HTTPStreamingIndexHandlerEvent.INDEX_READY,
						false,
						false,
						item.live,
						initialOffset
					)
				);
			}
			_fromDVR = false;
		}

		private function notifyTotalDuration(duration:Number, quality:int, live:Boolean):void{
			var sdo:FLVTagScriptDataObject = new FLVTagScriptDataObject();
			var metaInfo:Object = new Object();
			if(!live || (live && _indexInfo.dvrInfo))
				metaInfo.duration = duration;
			else
				metaInfo.duration = 0;

			sdo.objects = ["onMetaData", metaInfo];
			dispatchEvent(
				new HTTPStreamingEvent(
					HTTPStreamingEvent.SCRIPT_DATA,
					false,
					false,
					0,
					sdo,
					FLVTagScriptDataMode.IMMEDIATE
				)
			);
		}

		private function dispatchDVRStreamInfo(item:HTTPStreamingM3U8IndexRateItem):void{
			var dvrInfo:DVRInfo = _indexInfo.dvrInfo;
			if(dvrInfo == null) // Nothing todo here!
				return;

			dvrInfo.isRecording = item.live; // it's simple if not live then VOD =)
			var currentDuration:Number = item.totalTime;
			// make some cheating...
			var currentTime:Number = item.totalTime - ((item.totalTime/item.manifest.length) * 3);

			// update current length of the DVR window
			dvrInfo.curLength = currentTime;// because dvrInfo.startTime is "0.0" =);

			// adjust the start time if we have a DVR rooling window active
			if ((dvrInfo.windowDuration != -1) && (dvrInfo.curLength > dvrInfo.windowDuration))
			{
				dvrInfo.startTime += dvrInfo.curLength - dvrInfo.windowDuration;
				dvrInfo.curLength = dvrInfo.windowDuration;
			}

			dispatchEvent(new DVRStreamInfoEvent(
					DVRStreamInfoEvent.DVRSTREAMINFO,
					false,
					false,
					dvrInfo
				)
			);
		}

		private function updateRateItem(playlist:M3U8Playlist, item:HTTPStreamingM3U8IndexRateItem):void{
			// refresh manifest items
			for each(var m3u8Item:M3U8Item in playlist.streamItems){
				var iItem:HTTPStreamingM3U8IndexItem = new HTTPStreamingM3U8IndexItem(m3u8Item.duration, m3u8Item.url);
				item.addIndexItem(iItem);
			}

			item.setSequenceNumber(playlist.sequenceNumber);
			item.setLive(playlist.isLive);
		}
		protected var logger:Logger = Log.getLogger("org.denivip.osmf.plugins.hls.HTTPStreamingM3U8IndexHandler");
	}
}

fix incorrect seek for dvr

fix incorrect seek for dvr

correct duration value in notifyTotalDuration() func for DVR stream