require "weaver/version" require 'fileutils' require 'sinatra' module Weaver class Elements def initialize(anchors) @inner_content = [] @anchors = anchors end def method_missing(name, *args, &block) tag = "<#{name} />" if args[0].is_a? String inner = args.shift end if block elem = Elements.new(@anchors) elem.instance_eval(&block) inner = elem.generate end if !inner options = args[0] || [] opts = options.map { |key,value| "#{key}=\"#{value}\"" }.join " " tag = "<#{name} #{opts} />" elsif args.length == 0 tag = "<#{name}>#{inner}" elsif args.length == 1 and args[0].is_a? Hash options = args[0] opts = options.map { |key,value| "#{key}=\"#{value}\"" }.join " " tag = "<#{name} #{opts}>#{inner}" end @inner_content << tag tag end def icon(type) iconname = type.to_s.gsub(/_/, "-") i class: "fa fa-#{iconname}" do end end def ibox(&block) panel = Panel.new(@anchors) panel.instance_eval(&block) @inner_content << panel.generate end def panel(title, &block) div class: "panel panel-default" do div class: "panel-heading" do h5 title end div class: "panel-body", &block end end def image(name, options={}) img class: "img-responsive #{options[:class]}", src: "/images/#{name}" end def crossfade_image(image_normal, image_hover) div class: "crossfade" do image image_hover, class: "bottom" image image_normal, class: "top" end end def breadcrumb(patharray) ol class: "breadcrumb" do patharray.each do |path| li path end end end def p(*args, &block) method_missing(:p, *args, &block) end def text(theText) @inner_content << theText end def link(url, title=nil) if !title title = url end a href: url, target: "_blank" do span do text title text " " icon :external_link end end end def accordion(&block) acc = Accordion.new(@anchors) acc.instance_eval(&block) @inner_content << acc.generate end def widget(options={}, &block) #gray-bg #white-bg #navy-bg #blue-bg #lazur-bg #yellow-bg #red-bg #black-bg color = "#{options[:color]}-bg" || "navy-bg" div :class => "widget style1 #{color}", &block end def row(options={}, &block) r = Row.new(@anchors, options) r.instance_eval(&block) @inner_content << <<-ENDROW
#{r.generate}
ENDROW end def jumbotron(&block) div :class => "jumbotron", &block end def _button(options={}) anIcon = options[:icon] title = options[:title] if title.is_a? Hash options.merge! title title = anIcon anIcon = nil end style = options[:style] || :primary size = "btn-#{options[:size]}" if options[:size] block = "btn-block" if options[:block] outline = "btn-outline" if options[:outline] dim = "dim" if options[:threedee] dim = "dim btn-large-dim" if options[:bigthreedee] dim = "btn-rounded" if options[:rounded] dim = "btn-circle" if options[:circle] buttonOptions = { :type => "button", :class => "btn btn-#{style} #{size} #{block} #{outline} #{dim}" } type = :button buttonOptions[:"data-toggle"] = "button" if options[:toggle] type = :a if options[:toggle] method_missing type, buttonOptions do if title.is_a? Symbol icon title else icon anIcon if anIcon text " " if anIcon text title end end end def button(anIcon, title, options={}) options[:icon] = anIcon options[:title] = title _button(options) end def block_button(anIcon, title, options={}) options[:block] = true options[:icon] = anIcon options[:title] = title _button(options) end def outline_button(anIcon, title, options={}) options[:outline] = true options[:icon] = anIcon options[:title] = title _button(options) end def big_button(anIcon, title, options={}) options[:size] = :lg options[:icon] = anIcon options[:title] = title _button(options) end def small_button(anIcon, title, options={}) options[:size] = :sm options[:icon] = anIcon options[:title] = title _button(options) end def tiny_button(anIcon, title, options={}) options[:size] = :xs options[:icon] = anIcon options[:title] = title _button(options) end def embossed_button(anIcon, title, options={}) options[:threedee] = true options[:icon] = anIcon options[:title] = title _button(options) end def big_embossed_button(anIcon, title, options={}) options[:bigthreedee] = true options[:icon] = anIcon options[:title] = title _button(options) end def rounded_button(anIcon, title, options={}) options[:rounded] = true options[:icon] = anIcon options[:title] = title _button(options) end def circle_button(anIcon, title, options={}) options[:circle] = true options[:icon] = anIcon options[:title] = title _button(options) end def table_from_hashes(hashes) keys = {} hashes.each do |hash| hash.each do |key,value| keys[key] = "" end end table class: "table" do thead do keys.each do |key, _| th key.to_s end end hashes.each do |hash| tr do keys.each do |key, _| td hash[key] || " " end end end end end def generate @inner_content.join end end class Panel < Elements def initialize(anchors) super(anchors) @title = nil @footer = nil @type = :ibox @tabs = nil @body = true @extra = nil @min_height = nil end def generate inner = super types = { :ibox => { outer: "ibox float-e-margins",header: "ibox-title", body: "ibox-content" , footer: "ibox-footer"}, :panel => { outer: "panel panel-default", header: "panel-heading", body: "panel-body" , footer: "panel-footer"}, :primary => { outer: "panel panel-primary", header: "panel-heading", body: "panel-body" , footer: "panel-footer"}, :success => { outer: "panel panel-success", header: "panel-heading", body: "panel-body" , footer: "panel-footer"}, :info => { outer: "panel panel-info", header: "panel-heading", body: "panel-body" , footer: "panel-footer"}, :warning => { outer: "panel panel-warning", header: "panel-heading", body: "panel-body" , footer: "panel-footer"}, :danger => { outer: "panel panel-danger", header: "panel-heading", body: "panel-body" , footer: "panel-footer"}, :blank => { outer: "panel blank-panel", header: "panel-heading", body: "panel-body" , footer: "panel-footer"} } title = @title footer = @footer tabs = @tabs hasBody = @body extra = @extra classNames = types[@type] min_height = @min_height elem = Elements.new(@anchors) elem.instance_eval do div class: classNames[:outer] do if title or tabs div class: classNames[:header] do text title if title text tabs.generate_tabs if tabs end end if hasBody div class: classNames[:body], style: "min-height: #{min_height}px" do text inner text tabs.generate_body if tabs end end if extra text extra end if footer div class: classNames[:footer] do text footer end end end end elem.generate end def min_height(val) @min_height = val end def type(aType) @type = aType end def body(hasBody) @body = hasBody end def title(title=nil, &block) @title = title if block elem = Elements.new(@anchors) elem.instance_eval(&block) @title = elem.generate end end def extra(&block) if block elem = Elements.new(@anchors) elem.instance_eval(&block) @extra = elem.generate end end def footer(footer=nil, &block) @footer = footer if block elem = Elements.new(@anchors) elem.instance_eval(&block) @footer = elem.generate end end def tabs(&block) tabs = Tabs.new(@anchors) tabs.instance_eval(&block) @tabs = tabs end end class Accordion def initialize(anchors) @anchors = anchors @tabs = {} @paneltype = :panel @is_collapsed = false if !@anchors["accordia"] @anchors["accordia"] = [] end accArray = @anchors["accordia"] @accordion_name = "accordion#{accArray.length}" accArray << @accordion_name end def collapsed(isCollapsed) @is_collapsed = isCollapsed end def type(type) @paneltype = type end def tab(title, &block) if !@anchors["tabs"] @anchors["tabs"] = [] end tabArray = @anchors["tabs"] elem = Elements.new(@anchors) elem.instance_eval(&block) tabname = "tab#{tabArray.length}" tabArray << tabname @tabs[tabname] = { title: title, elem: elem } end def generate tabbar = Elements.new(@anchors) tabs = @tabs paneltype = @paneltype accordion_name = @accordion_name is_collapsed = @is_collapsed tabbar.instance_eval do div :class => "panel-group", id: accordion_name do cls = "panel-collapse collapse in" cls = "panel-collapse collapse" if is_collapsed tabs.each do |anchor, value| ibox do type paneltype body false title do div :class => "panel-title" do a :"data-toggle" => "collapse", :"data-parent" => "##{accordion_name}", href: "##{anchor}" do if value[:title].is_a? Symbol icon value[:title] else text value[:title] end end end end extra do div id: anchor, :class => cls do div :class => "panel-body" do text value[:elem].generate end end end end cls = "panel-collapse collapse" end end end tabbar.generate end end class Tabs def initialize(anchors) @anchors = anchors @tabs = {} end def tab(title, &block) if !@anchors["tabs"] @anchors["tabs"] = [] end tabArray = @anchors["tabs"] elem = Elements.new(@anchors) elem.instance_eval(&block) tabname = "tab#{tabArray.length}" tabArray << tabname @tabs[tabname] = { title: title, elem: elem } end def generate_body tabbar = Elements.new(@anchors) tabs = @tabs tabbar.instance_eval do div :class => "tab-content" do cls = "tab-pane active" tabs.each do |anchor, value| div id: "#{anchor}", :class => cls do text value[:elem].generate end cls = "tab-pane" end end end tabbar.generate end def generate_tabs tabbar = Elements.new(@anchors) tabs = @tabs tabbar.instance_eval do div :class => "panel-options" do ul :class => "nav nav-tabs" do cls = "active" tabs.each do |anchor, value| li :class => cls do a :"data-toggle" => "tab", href: "##{anchor}" do if value[:title].is_a? Symbol icon value[:title] else text value[:title] end end end cls = "" end end end end tabbar.generate end end class Page def initialize(title) @title = title @content = "" @body_class = nil @anchors = {} end def generate(back_folders, options={}) mod = "../" * back_folders style = <<-ENDSTYLE ENDSTYLE if options[:style] == :empty style = "" end body_tag = "" body_tag = "" if @body_class loading_bar = "" loading_bar = '' if @loading_bar_visible <<-SKELETON #{@title} #{style} #{body_tag} #{@content} #{loading_bar} SKELETON end end class Row attr_accessor :extra_classes def initialize(anchors, options) @columns = [] @free = 12 @extra_classes = options[:class] || "" @anchors = anchors end def twothirds(&block) opts = { xs: 12, sm: 12, md: 8, lg: 8 } col(4, opts, &block) end def half(&block) opts = { xs: 12, sm: 12, md: 12, lg: 6 } col(4, opts, &block) end def third(&block) opts = { xs: 12, sm: 12, md: 4, lg: 4 } col(4, opts, &block) end def quarter(&block) opts = { xs: 12, sm: 12, md: 6, lg: 3 } col(3, opts, &block) end def col(occupies, options={}, &block) raise "Not enough columns!" if @free < occupies elem = Elements.new(@anchors) elem.instance_eval(&block) @columns << { occupy: occupies, elem: elem, options: options } @free -= occupies end def generate @columns.map { |col| xs = col[:options][:xs] || col[:occupy] sm = col[:options][:sm] || col[:occupy] md = col[:options][:md] || col[:occupy] lg = col[:options][:lg] || col[:occupy] <<-ENDCOLUMN
#{col[:elem].generate}
ENDCOLUMN }.join end end class Menu attr_accessor :items def initialize() @items = [] end def nav(name, icon=:question, url=nil, &block) if url @items << { name: name, link: url, icon: icon } end if block menu = Menu.new menu.instance_eval(&block) @items << { name: name, menu: menu, icon: icon } end end end class NavPage < Page def initialize(title) super @menu = Menu.new end def menu(&block) @menu.instance_eval(&block) end end class SideNavPage < NavPage def initialize(title) @rows = [] super end def header(&block) row(class: "wrapper border-bottom white-bg page-heading", &block) end def row(options={}, &block) r = Row.new(@anchors, options) r.instance_eval(&block) @rows << r end def generate(level) rows = @rows.map { |row| <<-ENDROW
#{row.generate}
ENDROW }.join menu = @menu navigation = Elements.new(@anchors) navigation.instance_eval do menu.items.each do |item| li do if item.has_key? :menu a href:"#" do icon item[:icon] span :class => "nav-label" do text item[:name] end span :class => "fa arrow" do text "" end end ul :class => "nav nav-second-level" do item[:menu].items.each do |inneritem| li do if inneritem.has_key?(:menu) raise "Second level menu not supported" else a href:inneritem[:link] do text inneritem[:name] end end end end end elsif a href: "#" do span :class => "nav-label" do text item[:name] end end end end end end @loading_bar_visible = true @content = <<-ENDBODY
#{rows}
ENDBODY super end end class TopNavPage < NavPage def initialize(title) @rows = [] super end def nav(&block) end def header(&block) row(class: "wrapper border-bottom white-bg page-heading", &block) end def row(options={}, &block) r = Row.new(@anchors, options) r.instance_eval(&block) @rows << r end def generate(level) rows = @rows.map { |row| <<-ENDROW
#{row.generate}
ENDROW }.join @body_class = "top-navigation" @loading_bar_visible = true menu = @menu navigation = Elements.new(@anchors) navigation.instance_eval do menu.items.each do |item| li do if item.has_key? :menu li :class => "dropdown" do a :"aria-expanded" => "false", role: "button", href: "#", :class => "dropdown-toggle", :"data-toggle" => "dropdown" do icon item[:icon] text item[:name] span :class => "caret" do text "" end end ul role: "menu", :class => "dropdown-menu" do item[:menu].items.each do |inneritem| li do if inneritem.has_key?(:menu) raise "Second level menu not supported" else a href:inneritem[:link] do text inneritem[:name] end end end end end end elsif a href: "#" do span :class => "nav-label" do icon item[:icon] text item[:name] end end end end end end @content = <<-ENDBODY
#{rows}
ENDBODY super end end class CenterPage < Page def initialize(title, element) @element = element super(title) end def generate(level) @body_class = "gray-bg" @content = <<-CONTENT
#{@element.generate}
CONTENT super end end class Weave attr_accessor :pages def initialize(file) @pages = {} @file = file instance_eval(File.read(file), file) end def center_page(path, title, &block) elem = Elements.new({}) elem.instance_eval(&block) if block p = CenterPage.new(title, elem) @pages[path] = p end def sidenav_page(path, title, &block) p = SideNavPage.new(title) p.instance_eval(&block) if block @pages[path] = p end def topnav_page(path, title, &block) p = TopNavPage.new(title) p.instance_eval(&block) if block @pages[path] = p end def include(file) dir = File.dirname(@file) filename = File.join([dir, file]) File.read(filename) load filename end end end