Browse Source

rewrite for release

Juergen 'eTM' Mangler 1 year ago
parent
commit
74ad90be26

+ 0 - 1
.vimrc

@@ -1 +0,0 @@
-map <F9> :!./wiz -v restart<CR>

+ 2 - 0
AUTHORS

@@ -0,0 +1,2 @@
+Manuel Gall <gall.manuel.89@gmail.com>
+Juergen "eTM" Mangler <juergen.mangler@gmail.com>

File diff suppressed because it is too large
+ 644 - 40
LICENSE


+ 21 - 0
Rakefile

@@ -0,0 +1,21 @@
+require 'rake'
+require 'rubygems/package_task'
+
+spec = eval(File.read('cpee-frames.gemspec'))
+Gem::PackageTask.new(spec) do |pkg|
+  pkg.need_zip = true
+  pkg.need_tar = true
+  FileUtils.mkdir 'pkg' rescue nil
+  FileUtils.rm_rf Dir.glob('pkg/*')
+  FileUtils.ln_sf "#{pkg.name}.gem", "pkg/#{spec.name}.gem"
+end
+
+task :default => [:gem]
+
+task :push => :gem do |r|
+  `gem push pkg/cpee-logging-xes-yaml.gem`
+end
+
+task :install => :gem do |r|
+  `gem install pkg/cpee-logging-xes-yaml.gem`
+end

+ 25 - 0
cpee-frames.gemspec

@@ -0,0 +1,25 @@
+Gem::Specification.new do |s|
+  s.name             = "cpee-frames"
+  s.version          = "1.0.0"
+  s.platform         = Gem::Platform::RUBY
+  s.license          = "GPL-3.0"
+  s.summary          = "Dashboard management service with UI and backend for the cpee.org family of workflow management tools"
+
+  s.description      = "Flexibly arrange and display user interface components in a grid from executable process models. Very useful if you want to build dashboards."
+
+  s.files            = Dir['{server/**/*,tools/**/*,lib/**/*}'] + %w(LICENSE Rakefile cpee-frames.gemspec README.md AUTHORS)
+  s.require_path     = 'lib'
+  s.extra_rdoc_files = ['README.md']
+  s.bindir           = 'tools'
+  s.executables      = ['cpee-frames']
+
+  s.required_ruby_version = '>=3.0'
+
+  s.authors          = ['Manuell Gall', 'Juergen Mangler']
+
+  s.email            = 'gall.manuel.89@gmail.com'
+  s.homepage         = 'https://github.com/ManuelGall/cpee-frames'
+
+  s.add_runtime_dependency 'riddl', '~> 0.99'
+  s.add_runtime_dependency 'json', '~> 2.1'
+end

+ 0 - 551
frames

@@ -1,551 +0,0 @@
-#!/usr/bin/ruby
-#
-# This file is part of centurio.work/ing/commands.
-#
-# centurio.work/ing/commands is free software: you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or (at your
-# option) any later version.
-#
-# centurio.work/ing/commands is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
-# Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# centurio.work/ing/commands (file COPYING in the main directory). If not, see
-# <http://www.gnu.org/licenses/>.
-
-require 'rubygems'
-require 'json'
-require 'xml/smart'
-require 'riddl/server'
-require 'fileutils'
-require 'typhoeus'
-
-# process:
-# https://centurio.work/customers/evva/flow/?monitor=https://centurio.work/flow-test/engine/729/
-
-class GetTutorial < Riddl::Implementation
-  def response
-    Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','tutorial.html')))
-  end
-end
-
-
-class GetAllConfigs < Riddl::Implementation
-  def response  
-    Dir.chdir( __dir__  + '/data')
-    databack = JSON::pretty_generate(Dir.glob('*/').sort_by{|x| x.downcase});
-    Dir.chdir( __dir__)
-    Riddl::Parameter::Complex.new('list','application/json',databack)
-  end
-end
-
-
-class Get < Riddl::Implementation
-  def response
-    if @r[0] == 'test'
-      Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','test.html')))
-    elsif @r[0] == 'menu'
-      Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','menu.html')))
-    elsif @r[0] == 'framedata'
-      Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','framedata.html')))
-    else
-      Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','template.html')))
-    end
-  end
-end
-
-class InitFrame < Riddl::Implementation
-  def response
-    Dir.mkdir(File.join('data',@r.last)) rescue nil
-
-    if !@p[0].value.to_s.empty?
-      File.write(File.join('data',@r.last,'style.url'),@p[0].value)
-    end
-
-    File.write(File.join('data',@r.last,'frames.json'),JSON.dump(JSON.parse('{"data":[]}')))
-
-    #for handler
-    File.write(File.join('data',@r.last,'dataelements.json'),JSON.dump(JSON.parse('{"data":[]}')))
-
-    File.write(File.join('data',@r.last,'info.json'),JSON.dump(JSON.parse('{"x_amount":' + @p[2].value + ', "y_amount":' + @p[3].value + ', "lang":"' + @p[4].value  + '", "langs":["' + @p[4].value +  '"], "document_name": "' + @p[5].value + '"}')))
-
-    File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
-    File.write(File.join('data',@r.last,'cpeeinstance.url'),@h['CPEE_INSTANCE_URL'])
-
-    @a[0].send('new')
-    nil
-  end
-
-
-  #def headers
-  #  Riddl::Header.new('CPEE-CALLBACK', 'true')
-  #end
-end
-
-#https://coderwall.com/p/atyfyq/ruby-string-to-boolean
-#showbutton
-class String
-  def to_bool
-    return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
-    return false if self == false || self.empty? || self =~ (/(false|f|no|n|0)$/i)
-    raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
-  end
-end
-
-class NewFrameSet < Riddl::Implementation
-  def response
-    path = File.join('data',@r.last,'frames.json')
-    file = File.read(path)
-    data_hash = JSON::parse(file)
-
-    #check if new frame overlaps others if it does, delete overlapped frames
-    data_hash["data"].each do | c |
-      if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[1].value.to_i, @p[2].value.to_i, (@p[1].value.to_i + @p[3].value.to_i - 1), (@p[2].value.to_i + @p[4].value.to_i - 1))
-        data_hash["data"].delete(c)
-      end
-    end
-
-    #check if url is set
-    if @p[7].value != ""
-      urls = JSON::parse(@p[7].value);
-      
-      if @p[8].value == ""
-        hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value, style: @p[6].value, default: "{}", callback: @h['CPEE_CALLBACK']};
-      else
-        hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value,  style: @p[6].value, default: JSON::parse(@p[8].value), callback: @h['CPEE_CALLBACK']};
-      end
-
-      data_hash["data"].push(hash)
-      File.write(path, JSON.dump(data_hash))
-
-      #only send active url to client
-      infofile = File.join('data',@r.last,'info.json')
-      infojson = JSON::parse(File.read(infofile))
-      hash["url"] = urls.find{ |h| h['lang'] == infojson["lang"]}['url']
-
-
-      @a[0].send(JSON.dump(hash))
-    else
-      File.write(path, JSON.dump(data_hash))
-      hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: "empty", showbutton: @p[5].value, style: @p[6].value, default: "{}", callback: @h['CPEE_CALLBACK']};
-
-
-      @a[0].send(JSON.dump(hash))
-    end
-    
-    nil
-  end
-end
-
-class NewFrameWait < Riddl::Implementation
-  def response
-    path = File.join('data',@r.last,'frames.json')
-    file = File.read(path)
-    data_hash = JSON::parse(file)
-
-    #check if new frame overlaps others if it does, delete overlapped frames
-    data_hash["data"].each do | c |
-      if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[1].value.to_i, @p[2].value.to_i, (@p[1].value.to_i + @p[3].value.to_i - 1), (@p[2].value.to_i + @p[4].value.to_i - 1))
-        data_hash["data"].delete(c)
-      end
-    end
-
-
-    #check if url is set
-    if @p[7].value != ""
-      urls = JSON::parse(@p[7].value);
-      if @p[8].value == ""
-        hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value, style: @p[6].value, default: "{}", callback: @h['CPEE_CALLBACK']};
-      else
-        hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value, style: @p[6].value, default: JSON::parse(@p[8].value), callback: @h['CPEE_CALLBACK']};
-      end
-      data_hash["data"].push(hash)
-      File.write(path, JSON.dump(data_hash))
-
-      #only send active url to client
-      infofile = File.join('data',@r.last,'info.json')
-      infojson = JSON::parse(File.read(infofile))
-      hash["url"] = urls.find{ |h| h['lang'] == infojson["lang"]}['url']
-      
-      File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
-
-
-      @a[0].send(JSON.dump(hash))
-    else
-      File.write(path, JSON.dump(data_hash))
-      hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: "empty", showbutton: @p[5].value, style: @p[6].value, default: "{}", callback: @h['CPEE_CALLBACK']};
-      
-      File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
-
-
-      @a[0].send(JSON.dump(hash))
-      
-      Typhoeus.put(@h['CPEE_CALLBACK'], body: "No Frame Set")
-    end
-    nil    
-    
-  end
-  def headers
-    Riddl::Header.new('CPEE-CALLBACK', 'true')
-  end
-end
-
-class DeleteFrame < Riddl::Implementation
-  def response
-    path = File.join('data',@r.last,'frames.json')
-    file = File.read(path)
-    data_hash = JSON::parse(file)
-
-    data_hash["data"].each do | c |
-      if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[0].value.to_i, @p[1].value.to_i, (@p[0].value.to_i + 1), (@p[1].value.to_i + 1))
-        data_hash["data"].delete(c)
-      end
-    end
-
-    File.write(path, JSON.dump(data_hash))
-  end
-end
-
-def doOverlap(l1x, l1y, r1x, r1y, l2x, l2y, r2x, r2y)
-  if l1x > r2x || l2x > r1x
-      return false;
-  end
-  if l1y > r2y || l2y > r1y
-      return false;
-  end
-  return true;
-end
-
-class Delete < Riddl::Implementation
-  def response
-    pp "in delete"
-    if cbu = File.read(File.join('data',@r.last,'callback'))
-      pp "XYZ"
-      send = { 'operation' => @p[0].value }
-      case send['operation']
-        when 'result'
-          send['target'] = JSON::parse(@p[1].value.read)
-      end
-      cbu += '/' unless cbu[-1] == '/'
-      pp "Sending"
-      Typhoeus.put(cbu, body: JSON::generate(send), headers: { 'content-type' => 'application/json'})
-    end
-
-    #File.unlink(File.join('data',@r.last,'callback')) rescue nil
-    #File.unlink(File.join('data',@r.last,'cpeeinstance.url')) rescue nil
-    #File.unlink(File.join('data',@r.last,'style.url')) rescue nil
-    #File.unlink(File.join('data',@r.last,'document.xml')) rescue nil
-    #File.unlink(File.join('data',@r.last,'info.json')) rescue nil
-
-    @a[0].send('reset')
-    nil
-  end
-end
-
-class GetFrames < Riddl::Implementation #{{{
-  def response
-    fname = File.join('data',@r[-2],'frames.json')
-    if File.exists? fname
-
-      infofile = File.join('data',@r[-2],'info.json')
-      infojson = JSON::parse(File.read(infofile))
-
-      #remove not used languages
-      file = JSON::parse(File.read(fname))
-
-      file["data"].each do |child|
-        child["url"] = child["url"].find{ |h| h['lang'] == infojson["lang"]}['url']
-      end
-
-      Riddl::Parameter::Complex.new('value','application/json',JSON.dump(file))
-    else
-      @status = 404
-    end
-  end
-end #}}}
-
-class SetDataElements < Riddl::Implementation #{{{
-  def response
-    savejson = @p.map { |o| Hash[o.name, o.value] }.to_json
-    path = File.join('data',@r[0],'dataelements.json')
-    File.write(path, savejson)
-
-    #puts xyz
-
-    #puts JSON.pretty_generate(@p.to_json)
-
-
-
-    #puts @p.length()
-    #puts @p[0].name
-    #puts @p[0].value
-
-    #fname = File.join('data',@r[-2],'dataelements.json')
-    #if File.exists? fname
-    #  Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
-    #else
-    #  @status = 404
-    #end
-  end
-end #}}}
-
-class GetDataElements < Riddl::Implementation #{{{
-  def response
-    fname = File.join('data',@r[-2],'dataelements.json')
-    if File.exists? fname
-      Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
-    else
-      @status = 404
-    end
-  end
-end #}}}
-
-class GetInfo < Riddl::Implementation #{{{
-  def response
-    fname = File.join('data',@r[-2],'info.json')
-    if File.exists? fname
-      Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
-    else
-      @status = 404
-    end
-  end
-end #}}}
-
-class GetLangs < Riddl::Implementation #{{{
-  def response
-    fname = File.join('data',@r[-2],'info.json')
-    if File.exists? fname
-      infojson = JSON::parse(File.read(fname))
-      Riddl::Parameter::Complex.new('value','application/json',infojson["langs"])
-    else
-      @status = 404
-    end
-  end
-end #}}}
-
-class SetLang < Riddl::Implementation #{{{
-  def response
-    fname = File.join('data',@r[-2],'info.json')
-    if File.exists? fname
-      infojson = JSON::parse(File.read(fname))
-      infojson["lang"] = @p[0].value
-
-
-      #add to langs
-      if !infojson["langs"].include?(@p[0].value)
-        infojson["langs"].push(@p[0].value)
-      end
-
-      File.write(fname, JSON.dump(infojson))
-
-
-
-      @a[0].send('reset')
-      nil
-    else
-      @status = 404
-    end
-  end
-end #}}}
-
-class GetStyle < Riddl::Implementation #{{{
-  def response
-    fname = File.join('data',@r[-2],'style.url')
-    if File.exists? fname
-      Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
-    else
-      @status = 404
-    end
-  end
-end #}}}
-
-class GetCpeeInstance < Riddl::Implementation #{{{
-  def response
-    fname = File.join('data',@r[-2],'cpeeinstance.url')
-    if File.exists? fname
-      Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
-    else
-      @status = 404
-    end
-  end
-end #}}}
-
-class OutputTest < Riddl::Implementation #{{{
-  def response
-    puts "Test"
-  end
-end #}}}
-
-class Handler < Riddl::Implementation
-  def response
-    puts "handler"
-
-      topic         = @p[1].value
-      event_name    = @p[2].value
-      notification  = JSON.parse(@p[3].value.read)
-
-
-      instancenr = notification['instance']
-      content = notification['content']
-      activity = content['activity']
-      parameters = content['parameters']
-      receiving = content['received']
-
-      #puts instancenr
-      #puts activity
-      puts content['values']
-
-
-      if content['values']&.any?
-        #puts alldata['ausfuehrungen']
-        puts "writing file"
-        path = File.join('data',@r[0],'dataelements.json')
-        File.write(path, JSON.dump(content['values']))
-      end
-
-      @a[0].send(@r[0])
-      nil
-
-    nil
-  end
-end
-
-class SSE < Riddl::SSEImplementation #{{{
-  def onopen
-    signals = @a[0]
-    signals.add self
-    send 'started'
-    true
-  end
-
-  def onclose
-    signals = @a[0]
-    signals.remove self
-    nil
-  end
-end #}}}
-
-class SSE2 < Riddl::SSEImplementation #{{{
-  def onopen
-    signals = @a[0]
-    signals.add self
-    send 'started'
-    true
-  end
-
-  def onclose
-    signals = @a[0]
-    signals.remove self
-    nil
-  end
-end #}}}
-
-class Signaling # {{{
-  def initialize
-    @binding = []
-  end
-
-  def add(binding)
-    @binding << binding
-  end
-  def remove(binding)
-    @binding.delete(binding)
-  end
-  def length
-    @binding.length
-  end
-
-  def send(value)
-    @binding.each do |b|
-      b.send(value)
-    end
-  end
-end #}}}
-
-server = Riddl::Server.new(File.join(__dir__,'/frames.xml'), :host => 'localhost') do |opts|
-  accessible_description true
-  cross_site_xhr true
-
-  opts[:signals] = {}
-  opts[:signals2] = {}
-
-  parallel do
-    loop do
-      opts[:signals].each do |k,v|
-        v.send('keepalive')
-      end
-      opts[:signals2].each do |k,v|
-        v.send('keepalive')
-      end
-      sleep 5
-    end
-  end
-
-
-  on resource do
-    run GetTutorial if get
-    
-    on resource 'getConfigs' do
-        run GetAllConfigs if get
-    end
-
-    
-    
-    on resource do |r|
-      idx = r[:r][0]
-      opts[:signals][idx] ||= Signaling.new
-      opts[:signals2]["handler"] ||= Signaling.new
-
-      run Get, "test" if get
-      run InitFrame, opts[:signals][idx] if post 'input'
-
-      run NewFrameSet, opts[:signals][idx] if put 'sframe'
-      run NewFrameWait, opts[:signals][idx] if put 'wframe'
-
-      run DeleteFrame, opts[:signals][idx] if post 'deleteframe'
-
-      on resource 'handler' do
-        run Handler, opts[:signals2]["handler"] if post
-        on resource 'sse' do
-          run SSE2, opts[:signals2]["handler"] if sse
-        end
-      end
-
-      run Delete, opts[:signals][idx] if delete 'opa'
-      run Delete, opts[:signals][idx] if delete 'opb'
-      run Delete, opts[:signals][idx] if delete 'opc'
-      on resource 'sse' do
-        run SSE, opts[:signals][idx] if sse
-      end
-      on resource 'languages' do
-        run GetLangs if get
-        run SetLang, opts[:signals][idx] if post 'lang'
-      end
-      on resource 'style.url' do
-        run GetStyle if get
-      end
-      on resource 'cpeeinstance.url' do
-        run GetCpeeInstance if get
-      end
-      on resource 'info.json' do
-        run GetInfo if get
-      end
-      on resource 'frames.json' do
-        run GetFrames if get
-      end
-      on resource 'test' do
-        run OutputTest if put
-      end
-
-      on resource 'dataelements.json' do
-        run SetDataElements if post
-        run GetDataElements if get
-      end
-
-
-    end
-  end
-end.loop!

+ 0 - 1
frames.conf

@@ -1 +0,0 @@
-:port: 8305

+ 14 - 25
frames.xml

@@ -1,18 +1,18 @@
 <!--
-  This file is part of centurio.work/wiz.
+  This file is part of CPEE-FRAMES.
 
-  centurio.work/wiz is free software: you can redistribute it and/or modify it
-  under the terms of the GNU General Public License as published by the Free
-  Software Foundation, either version 3 of the License, or (at your option) any
-  later version.
+  CPEE-FRAMES is free software: you can redistribute it and/or modify it under
+  the terms of the GNU General Public License as published by the Free Software
+  Foundation, either version 3 of the License, or (at your option) any later
+  version.
 
-  centurio.work/wiz is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-  more details.
+  CPEE-FRAMES is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+  details.
 
   You should have received a copy of the GNU General Public License along with
-  centurio.work/wiz (file LICENSE in the main directory). If not, see
+  CPEE-FRAMES (file LICENSE in the main directory). If not, see
   <http://www.gnu.org/licenses/>.
 -->
 
@@ -77,22 +77,19 @@
   <message name="json">
     <parameter name="value" mimetype="application/json"/>
   </message>
-
-  <message name="string"> <!--{{{-->
+  <message name="string">
     <parameter name="value" type="string"/>
-  </message> <!--}}}-->
+  </message>
 
   <resource>
     <get out="ui"/>
-    
+
     <resource relative="getConfigs">
       <get out="ui"/>
     </resource>
-      
-      
+
     <resource>
       <get out="ui"/>
-
       <!-- create new Frame -->
       <post in='input'/>
       <!-- Set Frame Data with callback -->
@@ -100,13 +97,8 @@
       <!-- Set Frame Data without callback -->
       <put in='sframe'/>
 
-
-
       <post in='deleteframe'/>
 
-
-
-
       <delete in='opa'/>
       <delete in='opb'/>
       <delete in='opc'/>
@@ -134,15 +126,12 @@
         <post />
       </resource>
 
-
       <resource relative="test">
         <put in="string"/>
       </resource>
 
-
       <resource relative='handler'>
         <post />
-
         <resource relative='sse'>
           <sse/>
         </resource>

+ 539 - 0
lib/cpee-frames/implementation.rb

@@ -0,0 +1,539 @@
+#!/usr/bin/ruby
+#
+# This file is part of CPEE-FRAMES.
+#
+# CPEE-FRAMES is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# CPEE-FRAMES is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# CPEE-FRAMES (file LICENSE in the main directory). If not, see
+# <http://www.gnu.org/licenses/>.
+
+require 'rubygems'
+require 'json'
+require 'xml/smart'
+require 'riddl/server'
+require 'fileutils'
+require 'typhoeus'
+
+module CPEE
+
+  module Frames
+
+    SERVER = File.expand_path(File.join(__dir__,'frames.xml'))
+
+    # https://coderwall.com/p/atyfyq/ruby-string-to-boolean
+    # showbutton
+    refine String do #{{{
+      def to_bool
+        return true if self == true || self =~ (/(true|t|yes|y|1)$/i)
+        return false if self == false || self.empty? || self =~ (/(false|f|no|n|0)$/i)
+        raise ArgumentError.new("invalid value for Boolean: \"#{self}\"")
+      end
+    end #}}}
+
+    class GetTutorial < Riddl::Implementation #{{{
+      def response
+        Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'ui','tutorial.html')))
+      end
+    end #}}}
+
+    class GetAllConfigs < Riddl::Implementation #{{{
+      def response
+        data_dir = @a[0]
+        databack = JSON::pretty_generate(Dir.glob(File.join(data_dir,'*')).map{|f| File.basename(f)}.sort_by{|x| x.downcase})
+        Riddl::Parameter::Complex.new('list','application/json',databack)
+      end
+    end #}}}
+
+    class Get < Riddl::Implementation #{{{
+      def response
+        if @r[0] == 'test'
+          Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'ui','test.html')))
+        elsif @r[0] == 'menu'
+          Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'ui','menu.html')))
+        elsif @r[0] == 'framedata'
+          Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'ui','framedata.html')))
+        else
+          Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'ui','template.html')))
+        end
+      end
+    end #}}}
+
+    class InitFrame < Riddl::Implementation #{{{
+      def response
+        Dir.mkdir(File.join('data',@r.last)) rescue nil
+
+        if !@p[0].value.to_s.empty?
+          File.write(File.join('data',@r.last,'style.url'),@p[0].value)
+        end
+
+        File.write(File.join('data',@r.last,'frames.json'),JSON.dump(JSON.parse('{"data":[]}')))
+
+        #for handler
+        File.write(File.join('data',@r.last,'dataelements.json'),JSON.dump(JSON.parse('{"data":[]}')))
+
+        File.write(File.join('data',@r.last,'info.json'),JSON.dump(JSON.parse('{"x_amount":' + @p[2].value + ', "y_amount":' + @p[3].value + ', "lang":"' + @p[4].value  + '", "langs":["' + @p[4].value +  '"], "document_name": "' + @p[5].value + '"}')))
+
+        File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
+        File.write(File.join('data',@r.last,'cpeeinstance.url'),@h['CPEE_INSTANCE_URL'])
+
+        @a[0].send('new')
+        nil
+      end
+    end #}}}
+
+    class NewFrameSet < Riddl::Implementation
+      def response
+        path = File.join('data',@r.last,'frames.json')
+        file = File.read(path)
+        data_hash = JSON::parse(file)
+
+        #check if new frame overlaps others if it does, delete overlapped frames
+        data_hash["data"].each do | c |
+          if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[1].value.to_i, @p[2].value.to_i, (@p[1].value.to_i + @p[3].value.to_i - 1), (@p[2].value.to_i + @p[4].value.to_i - 1))
+            data_hash["data"].delete(c)
+          end
+        end
+
+        #check if url is set
+        if @p[7].value != ""
+          urls = JSON::parse(@p[7].value);
+
+          if @p[8].value == ""
+            hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value, style: @p[6].value, default: "{}", callback: @h['CPEE_CALLBACK']};
+          else
+            hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value,  style: @p[6].value, default: JSON::parse(@p[8].value), callback: @h['CPEE_CALLBACK']};
+          end
+
+          data_hash["data"].push(hash)
+          File.write(path, JSON.dump(data_hash))
+
+          #only send active url to client
+          infofile = File.join('data',@r.last,'info.json')
+          infojson = JSON::parse(File.read(infofile))
+          hash["url"] = urls.find{ |h| h['lang'] == infojson["lang"]}['url']
+
+
+          @a[0].send(JSON.dump(hash))
+        else
+          File.write(path, JSON.dump(data_hash))
+          hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: "empty", showbutton: @p[5].value, style: @p[6].value, default: "{}", callback: @h['CPEE_CALLBACK']};
+
+
+          @a[0].send(JSON.dump(hash))
+        end
+
+        nil
+      end
+    end
+
+    class NewFrameWait < Riddl::Implementation
+      def response
+        path = File.join('data',@r.last,'frames.json')
+        file = File.read(path)
+        data_hash = JSON::parse(file)
+
+        #check if new frame overlaps others if it does, delete overlapped frames
+        data_hash["data"].each do | c |
+          if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[1].value.to_i, @p[2].value.to_i, (@p[1].value.to_i + @p[3].value.to_i - 1), (@p[2].value.to_i + @p[4].value.to_i - 1))
+            data_hash["data"].delete(c)
+          end
+        end
+
+
+        #check if url is set
+        if @p[7].value != ""
+          urls = JSON::parse(@p[7].value);
+          if @p[8].value == ""
+            hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value, style: @p[6].value, default: "{}", callback: @h['CPEE_CALLBACK']};
+          else
+            hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: urls, showbutton: @p[5].value, style: @p[6].value, default: JSON::parse(@p[8].value), callback: @h['CPEE_CALLBACK']};
+          end
+          data_hash["data"].push(hash)
+          File.write(path, JSON.dump(data_hash))
+
+          #only send active url to client
+          infofile = File.join('data',@r.last,'info.json')
+          infojson = JSON::parse(File.read(infofile))
+          hash["url"] = urls.find{ |h| h['lang'] == infojson["lang"]}['url']
+
+          File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
+
+
+          @a[0].send(JSON.dump(hash))
+        else
+          File.write(path, JSON.dump(data_hash))
+          hash = {lx: @p[1].value.to_i, ly: @p[2].value.to_i, rx: (@p[1].value.to_i + @p[3].value.to_i - 1), ry: (@p[2].value.to_i + @p[4].value.to_i - 1), url: "empty", showbutton: @p[5].value, style: @p[6].value, default: "{}", callback: @h['CPEE_CALLBACK']};
+
+          File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
+
+
+          @a[0].send(JSON.dump(hash))
+
+          Typhoeus.put(@h['CPEE_CALLBACK'], body: "No Frame Set")
+        end
+        nil
+
+      end
+      def headers
+        Riddl::Header.new('CPEE-CALLBACK', 'true')
+      end
+    end
+
+    class DeleteFrame < Riddl::Implementation
+      def response
+        path = File.join('data',@r.last,'frames.json')
+        file = File.read(path)
+        data_hash = JSON::parse(file)
+
+        data_hash["data"].each do | c |
+          if doOverlap(c['lx'], c['ly'], c['rx'], c['ry'], @p[0].value.to_i, @p[1].value.to_i, (@p[0].value.to_i + 1), (@p[1].value.to_i + 1))
+            data_hash["data"].delete(c)
+          end
+        end
+
+        File.write(path, JSON.dump(data_hash))
+      end
+    end
+
+    def doOverlap(l1x, l1y, r1x, r1y, l2x, l2y, r2x, r2y)
+      if l1x > r2x || l2x > r1x
+          return false;
+      end
+      if l1y > r2y || l2y > r1y
+          return false;
+      end
+      return true;
+    end
+
+    class Delete < Riddl::Implementation
+      def response
+        pp "in delete"
+        if cbu = File.read(File.join('data',@r.last,'callback'))
+          pp "XYZ"
+          send = { 'operation' => @p[0].value }
+          case send['operation']
+            when 'result'
+              send['target'] = JSON::parse(@p[1].value.read)
+          end
+          cbu += '/' unless cbu[-1] == '/'
+          pp "Sending"
+          Typhoeus.put(cbu, body: JSON::generate(send), headers: { 'content-type' => 'application/json'})
+        end
+
+        #File.unlink(File.join('data',@r.last,'callback')) rescue nil
+        #File.unlink(File.join('data',@r.last,'cpeeinstance.url')) rescue nil
+        #File.unlink(File.join('data',@r.last,'style.url')) rescue nil
+        #File.unlink(File.join('data',@r.last,'document.xml')) rescue nil
+        #File.unlink(File.join('data',@r.last,'info.json')) rescue nil
+
+        @a[0].send('reset')
+        nil
+      end
+    end
+
+    class GetFrames < Riddl::Implementation #{{{
+      def response
+        fname = File.join('data',@r[-2],'frames.json')
+        if File.exists? fname
+
+          infofile = File.join('data',@r[-2],'info.json')
+          infojson = JSON::parse(File.read(infofile))
+
+          #remove not used languages
+          file = JSON::parse(File.read(fname))
+
+          file["data"].each do |child|
+            child["url"] = child["url"].find{ |h| h['lang'] == infojson["lang"]}['url']
+          end
+
+          Riddl::Parameter::Complex.new('value','application/json',JSON.dump(file))
+        else
+          @status = 404
+        end
+      end
+    end #}}}
+
+    class SetDataElements < Riddl::Implementation #{{{
+      def response
+        savejson = @p.map { |o| Hash[o.name, o.value] }.to_json
+        path = File.join('data',@r[0],'dataelements.json')
+        File.write(path, savejson)
+
+        #puts xyz
+
+        #puts JSON.pretty_generate(@p.to_json)
+
+
+
+        #puts @p.length()
+        #puts @p[0].name
+        #puts @p[0].value
+
+        #fname = File.join('data',@r[-2],'dataelements.json')
+        #if File.exists? fname
+        #  Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
+        #else
+        #  @status = 404
+        #end
+      end
+    end #}}}
+
+    class GetDataElements < Riddl::Implementation #{{{
+      def response
+        fname = File.join('data',@r[-2],'dataelements.json')
+        if File.exists? fname
+          Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
+        else
+          @status = 404
+        end
+      end
+    end #}}}
+
+    class GetInfo < Riddl::Implementation #{{{
+      def response
+        fname = File.join('data',@r[-2],'info.json')
+        if File.exists? fname
+          Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
+        else
+          @status = 404
+        end
+      end
+    end #}}}
+
+    class GetLangs < Riddl::Implementation #{{{
+      def response
+        fname = File.join('data',@r[-2],'info.json')
+        if File.exists? fname
+          infojson = JSON::parse(File.read(fname))
+          Riddl::Parameter::Complex.new('value','application/json',infojson["langs"])
+        else
+          @status = 404
+        end
+      end
+    end #}}}
+
+    class SetLang < Riddl::Implementation #{{{
+      def response
+        fname = File.join('data',@r[-2],'info.json')
+        if File.exists? fname
+          infojson = JSON::parse(File.read(fname))
+          infojson["lang"] = @p[0].value
+
+
+          #add to langs
+          if !infojson["langs"].include?(@p[0].value)
+            infojson["langs"].push(@p[0].value)
+          end
+
+          File.write(fname, JSON.dump(infojson))
+
+
+
+          @a[0].send('reset')
+          nil
+        else
+          @status = 404
+        end
+      end
+    end #}}}
+
+    class GetStyle < Riddl::Implementation #{{{
+      def response
+        fname = File.join('data',@r[-2],'style.url')
+        if File.exists? fname
+          Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
+        else
+          @status = 404
+        end
+      end
+    end #}}}
+
+    class GetCpeeInstance < Riddl::Implementation #{{{
+      def response
+        fname = File.join('data',@r[-2],'cpeeinstance.url')
+        if File.exists? fname
+          Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
+        else
+          @status = 404
+        end
+      end
+    end #}}}
+
+    class OutputTest < Riddl::Implementation #{{{
+      def response
+        puts "Test"
+      end
+    end #}}}
+
+    class Handler < Riddl::Implementation
+      def response
+        topic         = @p[1].value
+        event_name    = @p[2].value
+        notification  = JSON.parse(@p[3].value.read)
+
+        instancenr = notification['instance']
+        content = notification['content']
+        activity = content['activity']
+        parameters = content['parameters']
+        receiving = content['received']
+
+        #puts instancenr
+        #puts activity
+        puts content['values']
+
+
+        if content['values']&.any?
+          #puts alldata['ausfuehrungen']
+          puts "writing file"
+          path = File.join('data',@r[0],'dataelements.json')
+          File.write(path, JSON.dump(content['values']))
+        end
+
+        @a[0].send(@r[0])
+        nil
+      end
+    end
+
+    class SSE < Riddl::SSEImplementation #{{{
+      def onopen
+        signals = @a[0]
+        signals.add self
+        send 'started'
+        true
+      end
+
+      def onclose
+        signals = @a[0]
+        signals.remove self
+        nil
+      end
+    end #}}}
+
+    class SSE2 < Riddl::SSEImplementation #{{{
+      def onopen
+        signals = @a[0]
+        signals.add self
+        send 'started'
+        true
+      end
+
+      def onclose
+        signals = @a[0]
+        signals.remove self
+        nil
+      end
+    end #}}}
+
+    class Signaling # {{{
+      def initialize
+        @binding = []
+      end
+
+      def add(binding)
+        @binding << binding
+      end
+      def remove(binding)
+        @binding.delete(binding)
+      end
+      def length
+        @binding.length
+      end
+
+      def send(value)
+        @binding.each do |b|
+          b.send(value)
+        end
+      end
+    end #}}}
+
+    def self::implementation(opts)
+      opts[:signals] = {}
+      opts[:signals2] = {}
+      opts[:data_dir] ||= File.expand_path(File.join(__dir__,'data'))
+
+      Proc.new do
+        parallel do
+          loop do
+            opts[:signals].each do |k,v|
+              v.send('keepalive')
+            end
+            opts[:signals2].each do |k,v|
+              v.send('keepalive')
+            end
+            sleep 5
+          end
+        end
+
+        on resource do
+          run GetTutorial if get
+
+          on resource 'getConfigs' do
+            run GetAllConfigs, opts[:data_dir] if get
+          end
+
+          on resource do |r|
+            idx = r[:r][0]
+            opts[:signals][idx] ||= Signaling.new
+            opts[:signals2]["handler"] ||= Signaling.new
+
+            run Get, "test" if get
+            run InitFrame, opts[:signals][idx] if post 'input'
+
+            run NewFrameSet, opts[:signals][idx] if put 'sframe'
+            run NewFrameWait, opts[:signals][idx] if put 'wframe'
+
+            run DeleteFrame, opts[:signals][idx] if post 'deleteframe'
+
+            on resource 'handler' do
+              run Handler, opts[:signals2]["handler"] if post
+              on resource 'sse' do
+                run SSE2, opts[:signals2]["handler"] if sse
+              end
+            end
+
+            run Delete, opts[:signals][idx] if delete 'opa'
+            run Delete, opts[:signals][idx] if delete 'opb'
+            run Delete, opts[:signals][idx] if delete 'opc'
+            on resource 'sse' do
+              run SSE, opts[:signals][idx] if sse
+            end
+            on resource 'languages' do
+              run GetLangs if get
+              run SetLang, opts[:signals][idx] if post 'lang'
+            end
+            on resource 'style.url' do
+              run GetStyle if get
+            end
+            on resource 'cpeeinstance.url' do
+              run GetCpeeInstance if get
+            end
+            on resource 'info.json' do
+              run GetInfo if get
+            end
+            on resource 'frames.json' do
+              run GetFrames if get
+            end
+            on resource 'test' do
+              run OutputTest if put
+            end
+
+            on resource 'dataelements.json' do
+              run SetDataElements if post
+              run GetDataElements if get
+            end
+          end
+        end
+      end
+    end
+
+  end
+end

template/framedata.html → lib/cpee-frames/ui/framedata.html


template/menu.html → lib/cpee-frames/ui/menu.html


template/template.html → lib/cpee-frames/ui/template.html


template/test.html → lib/cpee-frames/ui/test.html


+ 11 - 11
template/tutorial.html

@@ -46,7 +46,7 @@
   </head>
   <body is="x-ui">
     <div id="container">
-    
+
       First Steps:<br>
         <ul>
 
@@ -57,7 +57,7 @@
           </ul>
           <li>define Data Element: (url where your frame will be reached)</li>
           <ul>
-            <li><a href="https://centurio.evva.com/out/frames/getConfigs">check if frame is not already in use </a></li>
+            <li><a href="getConfigs">check if frame is not already in use </a></li>
             <li>frameID: YourFrameName</a></li>
           </ul>
           <li>Create Init Activity</li>
@@ -81,8 +81,8 @@
           <li>Execute your Process</li>
           <li>Open your frame URL (add the defined name (YourFrameName) to the url currently open in this browser Window</li>
         </ul>
-        
-        
+
+
       Ohter Steps:<br>
         <ul>
 
@@ -106,7 +106,7 @@
             </ul>
           </ul>
         </ul>
-        
+
       There are more Ohter Steps?<br>
         <ul>
           <li>My Frame needs to Return something to the process engine Step1</li>
@@ -141,20 +141,20 @@
             <li>Nope, when one frame overlaps one or multiple frames the overlapped ones will be deleted automatically</li>
           </ul>
         </ul>
-        
-    
+
+
 
     I Would like to use the Forms service:<br>
       <ul>
         <li>Will be described soon ;)</li>
       </ul>
-        
+
     Data Handling:<br>
       <ul>
         <li>Will be described soon ;)</li>
       </ul>
-        
-    
+
+
     Other questions you could have: (note this is not other steps!)<br>
       <ul>
         <li>I want to do a circular iframe</li>
@@ -163,7 +163,7 @@
           <li>You have to manage this within your html site</li>
         </ul>
       </ul>
-    
+
     </div>
 
   </body>

+ 0 - 20
rngs/document.rng

@@ -1,20 +0,0 @@
-<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-  <start>
-    <element name="document">
-      <attribute name="id">
-        <data type="nonNegativeInteger"/>
-      </attribute>
-      <element name="label">
-        <data type="string"/>
-      </element>
-      <oneOrMore>
-        <element name="variant">
-          <attribute name="lang">
-            <data type="language"/>
-          </attribute>
-          <data type="string"/>
-        </element>
-      </oneOrMore>
-    </element>
-  </start>
-</grammar>

+ 0 - 13
rngs/langs.rng

@@ -1,13 +0,0 @@
-<grammar xmlns="http://relaxng.org/ns/structure/1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
-  <start>
-    <element name="languages">
-      <oneOrMore>
-        <element name="language">
-          <attribute name="lang">
-            <data type="language"/>
-          </attribute>
-        </element>
-      </oneOrMore>
-    </element>
-  </start>
-</grammar>

data/.gitignore → server/data/.gitignore


+ 38 - 0
server/frames

@@ -0,0 +1,38 @@
+#!/usr/bin/ruby
+#
+# This file is part of CPEE-FRAMES.
+#
+# CPEE-FRAMES is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# CPEE-FRAMES is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# CPEE-FRAMES (file LICENSE in the main directory). If not, see
+# <http://www.gnu.org/licenses/>.
+
+if File.exists?(File.join(__dir__,'..','lib','cpee-frames','implementation.rb'))
+  require_relative File.join(__dir__,'..','lib','cpee-frames','implementation')
+else
+  require 'cpee-frames/implementation'
+end
+
+options = {
+  :host => 'localhost',
+  :port => 8305,
+  :secure => false
+}
+
+Riddl::Server.new(CPEE::Frames::SERVER, options) do
+  accessible_description true
+  cross_site_xhr true
+
+  @riddl_opts[:data_dir] ||= File.join(__dir__,'data')
+
+  use CPEE::Frames::implementation(@riddl_opts)
+end.loop!

+ 2 - 0
server/frames.conf

@@ -0,0 +1,2 @@
+:port: 8305
+:host: localhost

+ 83 - 0
tools/cpee-frames

@@ -0,0 +1,83 @@
+#!/usr/bin/ruby
+#
+# This file is part of CPEE-FRAMES.
+#
+# CPEE-FRAMES is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# CPEE-FRAMES is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with CPEE-FRAMES (file LICENSE in the main directory). If not, see
+# <http://www.gnu.org/licenses/>.
+
+curpath = __dir__
+require 'rubygems'
+require 'optparse'
+require 'fileutils'
+require 'xml/smart'
+require 'yaml'
+require 'typhoeus'
+require 'stringio'
+
+def wrap(s, width=78, indent=18, extra_indent=4) #{{{
+	lines = []
+	line, s = s[0..indent-2], s[indent..-1]
+  s.split(/\n/).each do |ss|
+    ss.split(/[ \t]+/).each do |word|
+      if line.size + word.size >= width
+        lines << line
+        line = (" " * (indent + extra_indent)) + word
+      else
+        line << " " << word
+      end
+    end
+    lines << line if line
+    line = (" " * (indent-1))
+  end
+	return lines.join "\n"
+end #}}}
+
+exname = File.basename($0)
+
+ARGV.options { |opt|
+  opt.summary_indent = ' ' * 2
+  opt.summary_width = 15
+  opt.banner = "Usage:\n#{opt.summary_indent}#{exname} new [DIR] | newui [DIR]\n"
+  opt.on("Options:")
+  opt.on("--help", "-h", "This text") { puts opt; exit }
+	opt.on("")
+  opt.on(wrap("new [DIR]         scaffolds a sample logging service. Add a handler to a cpee instance to experience the pleasure.",78,18,0))
+	opt.on("")
+  opt.on(wrap("newui [DIR]       scaffolds css/js directors that are use by the UI into your webserver dir."))
+  opt.parse!
+}
+if (ARGV.length != 2)
+  puts ARGV.options
+  exit
+else
+  command = ARGV[0]
+  dir = ARGV[1]
+end
+
+if command == 'new'
+  if !File.exists?(dir)
+    FileUtils.cp_r(File.join(curpath,'..','server'),dir)
+    FileUtils.mkdir(File.join(dir,'data')) rescue nil
+  else
+    puts 'Directory already exists.'
+  end
+elsif command == 'newui'
+  if !File.exists?(dir)
+    FileUtils.cp_r(File.join(curpath,'..','ui'),dir)
+  else
+    puts 'Directory already exists.'
+  end
+else
+  puts ARGV.options
+end

template/css/BabelStoneFlags.ttf → ui/css/BabelStoneFlags.ttf


template/css/favicon-16x16.png → ui/css/favicon-16x16.png


template/css/favicon-32x32.png → ui/css/favicon-32x32.png


template/css/favicon-96x96.png → ui/css/favicon-96x96.png


template/css/frames.css → ui/css/frames.css


template/css/style.php → ui/css/style.php


template/js/frame_data.js → ui/js/frame_data.js


template/js/language.js → ui/js/language.js


template/js/test.js → ui/js/test.js


template/js/ui.js → ui/js/ui.js