class Object

Constants

BASE_DIR

require “awesome_print” require “jsmin”

Public Instance Methods

build_node(*args) { |xtype, options| ... } click to toggle source

recusive building node tree

# File lib/extjsml/generator.rb, line 31
def build_node(*args, &block)
  element = args[0]
  parent = args[1]
  params = args[2] || {}

  if parent.nil?
    k = element.keys.first
    v = element[k]
    xtype, options = ExtParser.parse(k)         
    options = yield(xtype, options) if block_given?
    xtype = ExtUtil.xtype_alias(xtype);
    node = eval("Ext#{xtype.capitalize}").new(options, parent)
    node.add_child build_node(v, node, &block)
    return node
  end

  if element.is_a? Hash
    begin
      wrapper_hash = ExtContainer.new({ layout: "fit", 
                                        autoHeight: true, 
                                        style: "{height: 100%; margin: 0em 0em;}", 
                                        defaults: { margins: "0 0"}}, 
      parent)
      element.each do |k,v|
        xtype, options = ExtParser.parse(k)             
        options = yield(xtype, options) if block_given?
        xtype = ExtUtil.xtype_alias(xtype);
        node = eval("Ext#{xtype.capitalize}").new(options, parent)
        node.add_child(build_node(v, node, &block))
        node.childs.each do |c|
          if c.xtype == "container" and 
            c.config[:layout] == "hbox" and 
            node.xtype == "container" and
            if c.child_of? "fieldset", "form"
              c.config.delete :labelWidth
              c.childs.each do |_c|
                # _c.config.delete :labelWidth
                _c.config.delete :defaults
                _c.config.delete :style
                _c.config.merge!({:margins => "0 0", :col_index => 0})
                _c.childs.each do |_f|
                  if ExtUtil.field_xtype.include? _f.xtype
                    # _f.config.delete :labelWidth
                    # break;
                  end
                end
                break;
              end
          end
          end
        end
        wrapper_hash.add_child node
      end 

      # skip wrapper
      if wrapper_hash.child_of?("viewport","container","grid","toolbar", "tabpanel", "form", "window", "editorgrid", "radiogroup", "checkboxgroup", "fieldset")
        # unwrap
        wrapper_hash = wrapper_hash.childs
      end
      wrapper_hash
    rescue Exception => e
      puts e.message, __FILE__, __LINE__, "in Hash" 
      raise e
    end
  elsif element.is_a? Array
    begin

      if element[0].is_a? Array
        new_element = element

        # default config
        inherit_config = {
          margins: "0 2.5"
        }

        # temp config of parent
        _config = {}
        if ExtUtil.container_xtype.include?(parent.xtype)
          # _config.merge!({ :padding => parent.config[:padding] }) if parent.config[:padding]
          _config.merge!({ :margins => parent.config[:margins] }) if parent.config[:margins]
        end
        inherit_config.merge! _config
        wrapper = ExtContainer.new({ layout: "hbox", 
                                     layoutConfig: { pack: "start", align: 'stretchmax'}, 
                                     defaults: inherit_config }, 
                                     parent)
        new_element.each_with_index do |el, col|
          wrapper.add_child(build_node(el, wrapper, { :col_index => col } , &block))
        end

        wrapper
      else
        node = ExtContainer.new({ layout: "anchor", 
                                  style: "{ height: 100%; margin: 0em 0em; }", 
                                  defaults: { margins: "0 0"}}, parent)
        element.each_with_index do |el, col|
          node.config.merge! params
          node.add_child(build_node(el, node, &block))
        end

        # check first child is the field component
        if node.childs.count > 0
          if ExtUtil.field_xtype.include?(node.childs.first.xtype) and 
            "toolbar" != parent.xtype
            # inherit fieldLableWidth
            _config = { :layout => "anchor" }
            _config = { :layout => "hbox" } if parent.xtype == 'fieldcontainer'
            pnode = node.find_parent("form","fieldset");
            # calculated proper labelWidth
            lw = []
            mlb = 0 # max label width
            offset = 15 # offset label width TODO ??
            is_contain_fieldset = false
            node.childs.each  do |c|
              next if c.xtype == "hidden"
              is_contain_fieldset = true if c.xtype == "fieldset"
              if ["radiogroup","checkboxgroup"].include? c.xtype
                lb = [] 
                c.childs.each do |r|
                  _lb = r.config[:boxLabel].size * ExtUtil.FontWidthRatio + offset unless r.config[:boxLabel].nil?
                  lb << _lb
                  r.override_config :width => _lb + offset 
                end
                if lb.max > 0 and not c.config[:width]
                  lb_width = lb.inject(&:+)
                  # c.override_config :width => (lb.max * lb.count) if lb.max > 0
                  c.override_config :width => lb_width
                end
              end
              lw << c.config[:fieldLabel].size * ExtUtil.FontWidthRatio + offset unless c.config[:fieldLabel].nil?
            end

            if parent.config[:fieldLabel]
              lw << parent.config[:fieldLabel].size * ExtUtil.FontWidthRatio + offset
            end

            _config.merge! :labelWidth => lw.max || 10 if pnode and pnode.config[:labelWidth].nil?

            node.override_config _config
            node.config.merge! :defaults => { :labelWidth => lw.max }

            # fix for chrome 21.x
            # container should have layout auto for fieldset
            if is_contain_fieldset
              node.override_config :layout => "auto"
            end

            if parent and parent.xtype == "container" and parent.config[:layout] == "hbox"
              parent.config.merge! :labelWidth => lw.max 
            end
            # end

            # update config if setting fileuploadfield
            if node.childs.any? {|n| n.xtype == "fileuploadfield"}
              f = node.find_parent("form")
              unless f.nil?
                f.config.merge! :frame => true, :fileUpload => true 
              else
                puts "warning: fileupload may need to be rendered under a form"
              end
            end

          end
        end

        # unwrap
        if node.child_of?("grid","toolbar", "editorgrid", "radiogroup", "checkboxgroup") or ["fieldset", "tabpanel","form", "window", "toolbar", "compositefield", "fieldcontainer"].include? parent.xtype
          unless _config.nil? and parent
            parent.config.merge! _config
            #puts "#{parent.xtype}, #{_config}"
          end
          node = node.childs
        end

        node
      end
    rescue Exception => e
      puts e.message, e.backtrace, __FILE__, __LINE__, "in Array of"
      raise e
    end

  elsif element.is_a? String
    # leaf node
    xtype, options = ExtParser.parse(element)           
    options = yield(xtype, options) if block_given?
    xtype = ExtUtil.xtype_alias(xtype);
    node = eval("Ext#{xtype.capitalize}").new(options, parent)
  end
end
compile_jext(yaml_str, js_class, options={}) click to toggle source
# File lib/extjsml/generator.rb, line 221
def compile_jext(yaml_str, js_class, options={})
  original_source = yaml_str.read
  preload_state = /^:?preload.*/.match original_source
  preload_source = false
  if preload_state
    preload_state = YAML.load(preload_state.to_s)
    preload = preload_state[:preload].split(/\s+/)
    if preload.is_a? Array
      preload_source = []
      preload.each do |path|
        path.gsub!(/^\.\//, '')
        extend_path = "#{options[:filedir]}/#{path}"
        preload_source << "#{path}:\n"
        another_script = File.open(extend_path, 'rb') do |f|
          while not f.eof
            l = f.readline
            preload_source << "  #{l}"
          end
        end
      end
    end
  end

  load_source = preload_source ? preload_source.join('') + original_source : original_source
  # puts load_source
  ast = YAML.load(load_source)
  # ap ast
  # require "awesome_print"
  # separate layout and config
  layout = ast[:layout]
  raise "require layout" if layout.nil?

  config = ast[:config] || ast['config'] || {}
  engine = ast[:engine] || ast['engine'] || {}

  js_class = engine[:class] || js_class.capitalize

  config.each do |k,v|
    if v.has_key? :as
      # TODO nested as
      # lookup shared config
      begin
        sharedconfig = YAML.load(File.open("#{BASE_DIR}/sharedconfig/#{v[:as]}.yaml","r"))
      rescue Exception => e
        p "raise", __FILE__, __LINE__
        puts "not found #{v[:as]} config in #{BASE_DIR}/sharedconfig/"
      end
      v.delete :as
      v = sharedconfig.merge v
      config[k] = v
    end
  end unless config.nil?

  begin
    root_node = build_node(layout, nil) do |xtype, opt|

      unless config.nil?
        opt.merge! config[xtype] unless config[xtype].nil?
        opt.merge! config[opt[:id]] unless config[opt[:id]].nil?
      end

      if opt[:as]
        # read sharedconfig
        begin
          sharedconfig = YAML.load(File.open("#{BASE_DIR}/sharedconfig/#{opt[:as]}.yaml","r"))
          opt = sharedconfig.merge opt
        rescue Exception => e
          puts "#{e.message} not found #{opt[:as]} config in #{BASE_DIR}/sharedconfig/"
        end
        opt.delete :as
      end

      opt
    end
  rescue Exception => e
    p "raise", __FILE__, __LINE__, e
    raise e
  end

  # tree has built

  # set to_extjs option
  engine.merge! options
  ExtNode.set_generator_config(engine)

  # extjs_tree = root_node.to_extjs
  event_template = []
  ExtNode.get_events.each do |el, h|
    event_template << "self.#{el}.on(\"#{h[0]}\", self.#{h[1]}, self);"
  end

  unless engine[:with_requirejs]

    ui_class_content = %Q{ 
    #{js_class}UI = Ext.extend(#{root_node.xtype.to_extclassname},{
    #{JSON.pretty_generate(root_node.config).gsub!(/\{|\}/,"").strip!},
  initComponent: function(){
    Ext.applyIf(this,#{root_node.config={};nil}
    #{JSON.pretty_generate(root_node.to_extjs, { space: "", max_nesting: 50})});
    #{js_class}UI.superclass.initComponent.call(this);
    var self = this;
    #{event_template.join("\n")}
    #{ExtNode.get_refs.map{|id, ref| "self._#{ref} = Ext.getCmp(\"#{id}\");"}.join("\n")}
  }
});
    }.strip
  else

    ui_class_content = %Q{ 
define(function(){
  var #{js_class}UI = Ext.extend(#{root_node.xtype.to_extclassname},{
    #{JSON.pretty_generate(root_node.config).gsub!(/\{|\}/,"").strip!},
    initComponent: function(){
      Ext.applyIf(this,#{root_node.config={};nil}
      #{JSON.pretty_generate(root_node.to_extjs, { space: "", max_nesting: 50})});
      #{js_class}UI.superclass.initComponent.call(this);
      var self = this;
      #{event_template.join("\n")}
      // manually make auto ref
      Ext.each(self.query('*'), function(element, index){
        if(element.cmp_id) {
          self["_" + element.cmp_id] = element;
        }
      });
    }
  });

  return #{js_class}UI;
});
      }.strip
  end

  # INQUIRY
  eve_inqury = ""
  if engine[:mode] == "inqury"
    # Must have base
    base_model = engine[:base]
    # TODO
    eve_inqury = %Q{
    self.search.on({
      click: function(){
        if(self.frm.isValid()){
          var params = Ext.apply(self.frm.serializeObject({ onlyDirty: false}), { model: "#{base_model}" });
          self.grd.fetch(params);
        }
      }
    });

    self.toggleWidth.on({
        click: function() {
            var el_id = "#" + this.el.id;
            var filter_id = "#" + self.frm.id;
            var pressed = $(el_id).attr("is_pressed");
            if(pressed == "t"){
                $(el_id).attr("is_pressed", "f");
                this.setText("Expand Width");
                self.grd.setSize(780); // TODO
                self.grd.doLayout();
                $(filter_id).parent().fadeIn();
            } else{
                $(el_id).attr("is_pressed", "t");
                this.setText("Reduce Width");
                var w = $(window.document).width() - 50;
                self.grd.setSize(w);
                self.grd.doLayout();
                $(filter_id).parent().hide();
            }
        }
    });

    self.exportXlsx.on({
      click: function() {
        if(self.frm.isValid()){
          var params = Ext.apply(self.frm.serializeObject({ onlyDirty: false}), { model: "#{base_model}", export_xlsx: true });

          if(self.grd.store.getSortState()){
            var sort = self.grd.store.getSortState();

            params = Ext.apply(params, {
              sort: sort.field,
              dir:  sort.direction
            });
          }

          var url = "/inquiry?" + Ext.urlEncode(params);

          window.open(url, "_blank")
        }
      }
    });

    self.exportCsv.on({
      click: function() {
        if(self.frm.isValid()){
          var params = Ext.apply(self.frm.serializeObject({ onlyDirty: false}), { model: "#{base_model}", export_csv: true });

          if(self.grd.store.getSortState()){
            var sort = self.grd.store.getSortState();

            params = Ext.apply(params, {
              sort: sort.field,
              dir:  sort.direction
            });
          }

          var url = "/inquiry?" + Ext.urlEncode(params);

          window.open(url, "_blank")
        }
      }
    });

    self.exportPdf.on({
      click: function() {
        if(self.frm.isValid()){
          var params = Ext.apply(self.frm.serializeObject({ onlyDirty: false}), { model: "#{base_model}", export_pdf: true });

          if(self.grd.store.getSortState()){
            var sort = self.grd.store.getSortState();

            params = Ext.apply(params, {
              sort: sort.field,
              dir:  sort.direction
            });
          }

          var url = "/inquiry?" + Ext.urlEncode(params);

          window.open(url, "_blank")
        }
      }
    });

    self.grd.on({
      datachanged: function  () {
          var st = self.grd.getStore();
          try {

            if(st && st.reader.jsonData.grand_summary){
              var summary = st.reader.jsonData.grand_summary;
              self.grandSummary.fillData(summary);
            }

            if(st && st.reader.jsonData.page_summary){
              var summary = st.reader.jsonData.page_summary;
              self.pageSummary.fillData(summary);
            }

          } catch(e){
            console.log("summary stuff " + e);
          }
      }
    });

    }
  end
  #################################################

  # REPORT
  eve_report = ""
  if engine[:mode] == "report"
    eve_inqury = %Q{
    self.search.on({
        click: function() {
          var f = $("iframe")
          if(f){
            var url = f.attr("src");
            var ff = f[0];
            var fragments = url.split("?");
            var host = fragments[0];
            var params = fragments[1];
            params = Ext.urlDecode(params);

            // reset params
            for(var k in params){
              console.log("key", k)
              if(k != "cmd" && k != "code"){
                delete params[k];
              }
            }
            var filter = self.frm.serializeObject();

            var src = host + "?" + Ext.urlEncode(Ext.apply(params, filter));
            ff.contentWindow.location = src;
          } else{
            Ext.Notify.error("Cannot Render");
          }
        }
    });
    }
  end

  #################################################

  unless engine[:with_requirejs]
    event_class_content = %Q{ 
    #{js_class} = Ext.extend(#{js_class}UI,{
  constructor: function  (config) {
    config = config || {};
    Ext.apply(this, config);
    #{js_class}.superclass.constructor.call(this)
  },

  initComponent: function(){
    #{js_class}.superclass.initComponent.call(this);
    var self = x = this;
    // use x for debugging at console
    #{eve_report}
    #{eve_inqury}
  }
});
    }.strip
  else
    event_class_content = %Q{ 
define(["./#{js_class}.ui"], function(#{js_class}UI){
  var #{js_class} = Ext.extend(#{js_class}UI,{
    constructor: function  (config) {
      config = config || {};
      Ext.apply(this, config);
    #{js_class}.superclass.constructor.call(this)
    },

    initComponent: function(){
    #{js_class}.superclass.initComponent.call(this);
      var self = x = this;
      // use x for debugging at console
    #{eve_report}
    #{eve_inqury}
    }
  });

  return #{js_class};
});
    }.strip
  end # end engine.with_requirejs

  # replace js code
  if ui_class_content.match(/<js>(.*)<\/js>/im)
    buf = []
    ui_class_content.each_line do |line|
      jscode = line.match(/<js>(.*)<\/js>/im)
      unless jscode.nil?
        line.gsub!(/\"[^:]*<js>(.*)<\/js>.*\"/im,'\1') 
        line.gsub!(/\\n/,"")
        line.gsub!(/\\\"/,'"')
      end
      # TODO
      # buf << JSMin.minify(line)
      buf << line
    end
    ui_class_content = buf.join("").strip
  end

  ExtNode.reset_generator_config

  return {ui_class_content: ui_class_content,
          event_class_content: event_class_content}
end