module Envjs::Runtime

Public Class Methods

extended(object) click to toggle source
# File lib/envjs/runtime.rb, line 10
  def self.extended object
    object.instance_eval do

      outer = nil
      scripts = {}
      
      master = global["$master"] = evaluate("new Object", nil, nil, nil, global)
      
      ( class << self; self; end ).send :define_method, :master do
        master
      end

      ( class << self; self; end ).send :define_method, :evaluate do |*args|
        ( script, file, line, global, scope, fn ) = *args
        scope ||= outer["$inner"]
        raise "cannot evaluate nil script" if script.nil?
        raise "cannot evaluate without a scope" if scope.nil?
        raise "outer given when inner needed" if !scope == global and !scope["isInner"]
        # print "eval in " + script[0,50].inspect + scope.inspect + " " + ( scope ? scope.isInner.inspect : "none" ) + "\n"
        global = nil
        # scope ||= inner
        if fn
          compiled_script = scripts[fn]
        end
        compiled_script ||= compile(script, file, line, global)
        raise "hell" if !compiled_script
        if fn && !scripts[fn]
          scripts[fn] = compiled_script
        end
        save = master["first_script_window"]
        if false
          p scope
          if master["first_script_window"]
            print "ignored: " +  ( scope["location"] ? scope["location"]["href"] : "nil" ) + " using " + ( master["first_script_window"]["location"] ?  master["first_script_window"]["location"]["href"] : "nil" ) + "\n"
          else
            print "pushing into " + ( scope["location"] ? scope["location"]["href"] : "nil" ) + "\n"
          end
        end

        master["first_script_window"] ||= scope
        raise "hell" if !master["first_script_window"]["isInner"] && master["first_script_window"] != self.global
        v = nil
        begin
          v = evaluate_compiled_script(compiled_script,scope)
          # p "pe", v, compiled_script, scope
        rescue Exception => e
          # p "oopsrt", e
          raise e
        ensure
          master["first_script_window"] = save
        end
        # print "done\n"
        v
      end

      evaluate( "print = function() {
  var l = arguments.length
  for( var i = 0; i < l; i++ ) {
    var s;
    if ( arguments[i] === null ) {
      s = "null";
    } else if ( arguments[i] === undefined  ) {
      s = "undefined"      
    } else {
      s = arguments[i].toString();
    }
    Ruby.print(s);
    if( i < l-1 ) {
      Ruby.print(" ");
    }
  }
  Ruby.print("\n");
  Ruby['$stdout'].flush();
};
", nil, nil, nil, global )

      evaluate "debug = function() {
  var l = arguments.length
  for( var i = 0; i < l; i++ ) {
    var s;
    if ( arguments[i] === null ) {
      s = "null";
    } else if ( arguments[i] === undefined  ) {
      s = "undefined"      
    } else {
      s = arguments[i].toString();
    }
    Ruby['$stderr'].print(s);
    if( i < l-1 ) {
      Ruby['$stderr'].print(" ");
    }
  }
  Ruby['$stderr'].print("\n");
  Ruby['$stderr'].flush();
};
", nil, nil, nil, global

      evaluate "puts = function() {
  var l = arguments.length
  for( var i = 0; i < l; i++ ) {
    var s;
    if ( arguments[i] === null ) {
      s = "null";
    } else if ( arguments[i] === undefined  ) {
      s = "undefined"      
    } else {
      s = arguments[i].toString();
    }
    Ruby.print(s);
    Ruby.eval("$stdout.flush")
  }
};
", nil, nil, nil, global

      master["runtime"] = self
      window_index = -1
      master["next_window_index"] = lambda { window_index += 1 }
      master.symbols = [ "Johnson", "Ruby", "print", "debug", "puts", "load", "reload", "whichInterpreter", "multiwindow", "seal" ]
      master.symbols.each { |symbol| master[symbol] = global[symbol] }
      master["seal"] = lambda do |*args|
        object, deep = *args
        seal object, deep
      end

      master.whichInterpreter = "Johnson"

      master.multiwindow = true

      # calling this from JS is hosed; the ruby side is confused, maybe because HTTPHeaders is mixed in?
      master.add_req_field = lambda { |r,k,v| r.add_field(k,v) }

      top_level_js = nil

      add_dep = nil
      
      add_dep = lambda do |w, f|
        if $envjsrb_wake
          $wake_info << "##file://#{f}"
        end
      end

      (class << self; self; end).send :define_method, :top_level_load do |path|
        if $envjsrb_wake
          $wake_info << "##{path}" if path
        end
      end

      master.load = lambda { |*files|
        if files.length == 2 && !(String === files[1])
          # now = Time.now
          f = files[0]
          w = files[1]
          # p "load", f, w

          # Hmmm ...
          uri = URI.parse f

          if uri.scheme == nil
            uri.scheme = "file"
            begin
              uri.path = Pathname.new(uri.path).realpath.to_s
            rescue Errno::ENOENT; end
            uri = URI.parse uri.to_s
          end

          uri_s = uri.to_s.sub %r(^file:/([^/])), 'file:///\1'

          if uri.scheme == "file"
            uri_s = uri.path
          elsif uri.scheme == "data"
            raise "implement 0"
          end

          v = open(uri_s).read.gsub(/\A#!.*$/, '')
          loc = nil
          add_dep.call w, f
          evaluate(v, f, 1, w, w, f)
        else
          load *files
        end
      }

      ( class << self; self; end ).send :define_method, :load do |*files|
        files.map { |f|
          # Hmmm ...

          uri = URI.parse f
          if uri.scheme == nil
            uri.scheme = "file"
            begin
              uri.path = Pathname.new(uri.path).realpath.to_s
            rescue Errno::ENOENT; end
            uri = URI.parse uri.to_s
          end
          uri_s = uri.to_s.sub %r(^file:/([^/])), 'file:///\1'
          
          if uri.scheme == "file"
            begin
              super uri.path
            rescue Exception => e
              if outer["$inner"]["onerror"]
                # outer["$inner"]["onerror"].call e
                evaluate("function(fn,scope,e){fn.call(scope,e)}").call(outer["$inner"]["onerror"], outer["$inner"], e)
              else
                raise e
              end
            end
          elsif uri.scheme == "data"
            raise "implement 1"
          elsif uri.scheme == "javascript"
            evaluate(URI.decode(uri.opaque),URI.decode(uri_s),1)
          else
            raise "hell 1: " + uri.inspect
          end

          # v = open(uri_s).read.gsub(/\A#!.*$/, '')
          # loc = nil
          # add_dep.call w, f
          # evaluate(v, f, 1, w, w, f)
          # evaluate(File.read(f).gsub(/\A#!.*$/, ''), f, 1)

        }.last
      end

      master.reload = lambda { |*files|
        if files.length == 2 && !(String === files[1])
          f = files[0]
          w = files[1]
          v = open(f).read.gsub(/\A#!.*$/, '')
          loc = nil
          add_dep.call w, f
          reevaluate(v, f, 1, w, w, f)
        else
          reload *files
        end
      }

      master.evaluate = lambda { |v,w|
        evaluate(v,"inline",1,w,w);
      }

      master.new_split_global_outer = lambda { new_split_global_outer }
      master.new_split_global_inner = lambda { |outer,_| new_split_global_inner outer }

      # create an proto window object and proxy

      outer = new_split_global_outer
      inner = new_split_global_inner( outer )

      master.symbols.each do |symbol|
        inner[symbol] = master[symbol]
      end

      inner["$inner"] = inner
      inner["$master"] = master
      inner["$options"] = evaluate("new Object", nil, nil, nil, inner);
      inner["$options"].proxy = outer

      inner.evaluate = lambda { |s|
        return master.evaluate.call(s,inner);
      }

      inner.load = lambda { |*files|
        files.each do |f|
          master['load'].call f, inner
        end
      }

      inner.reload = lambda { |*files|
        files.each do |f|
          master.reload.call f, inner
        end
      }

      ( class << self; self; end ).send :define_method, :wait do
        master["finalize"] && master.finalize.call
        master.eventLoop && master.eventLoop.wait
      end

      ( class << self; self; end ).send :define_method, :_become_first_script_window do
        # p "heh ++++++++++++++++++++++++++++", inner, master.first_script_window
        inner = master.first_script_window
      end

      ( class << self; self; end ).send :define_method, :reevaluate do |*args|
        ( script, file, line, global, scope, fn ) = *args
        raise "cannot evaluate nil script" if script.nil?
        # print "eval in " + script[0,50].inspect + (scope ? scope.toString() : "nil") + "\n"
        global = nil
        scope ||= inner
        compiled_script = compile(script, file, line, global)
        if fn
          scripts[fn] = compiled_script
        end
        begin
          evaluate_compiled_script(compiled_script,scope)
        rescue Exception => e
          p e
          raise e
        end
      end

      ( class << self; self; end ).send :define_method, :"[]" do |key|
        # key == "this" && evaluate("this", nil, nil, nil, inner) || @envjs[key]
        key == "this" && outer || outer[key]
      end

      ( class << self; self; end ).send :define_method, :"[]=" do |k,v|
        # inner[k] = v
        outer[k] = v
      end

      master['load'].call Envjs::EVENT_LOOP, global
      
      static = new_global
      
      master.symbols.each do |symbol|
        static[symbol] = master[symbol]
      end

      static["$master"] = master

      # fake it ...
      static["isInner"] = true
      master['load'].call Envjs::STATIC, static
      master["static"] = static

      master['load'].call Envjs::ENVJS, inner

      inner = nil
    end
  end