frames 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. #!/usr/bin/ruby
  2. #
  3. # This file is part of centurio.work/ing/commands.
  4. #
  5. # centurio.work/ing/commands is free software: you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or (at your
  8. # option) any later version.
  9. #
  10. # centurio.work/ing/commands is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
  13. # Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along with
  16. # centurio.work/ing/commands (file COPYING in the main directory). If not, see
  17. # <http://www.gnu.org/licenses/>.
  18. require 'rubygems'
  19. require 'json'
  20. require 'xml/smart'
  21. require 'riddl/server'
  22. require 'fileutils'
  23. require 'typhoeus'
  24. class Get < Riddl::Implementation
  25. def response
  26. if @r[0] == 'test'
  27. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','test.html')))
  28. else
  29. Riddl::Parameter::Complex.new('ui','text/html',File.open(File.join(__dir__,'template','template.html')))
  30. end
  31. end
  32. end
  33. class Put < Riddl::Implementation
  34. def response
  35. puts "AAAAAAAAAAAAAAAAAAA"
  36. Dir.mkdir(File.join('data',@r.last)) rescue nil
  37. File.write(File.join('data',@r.last,'style.url'),@p[0].value)
  38. File.write(File.join('data',@r.last,'document.xml'),@p[1].value)
  39. File.write(File.join('data',@r.last,'frames.json'),JSON.dump(JSON.parse('{"data":[]}')))
  40. if @p[2]&.name == 'info'
  41. File.write(File.join('data',@r.last,'info.json'),@p[2].value)
  42. end
  43. File.write(File.join('data',@r.last,'callback'),@h['CPEE_CALLBACK'])
  44. @a[0].send('new')
  45. nil
  46. end
  47. def headers
  48. Riddl::Header.new('CPEE-CALLBACK', 'true')
  49. end
  50. end
  51. class NewFrame < Riddl::Implementation
  52. def response
  53. path = File.join('data',@r.last,'frames.json')
  54. file = File.read(path)
  55. data_hash = JSON::parse(file)
  56. #puts "Before " + JSON.dump(data_hash);
  57. data_hash["data"].each do | c |
  58. 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 + @p[2].value.to_i - 1), (@p[1].value.to_i + @p[3].value.to_i - 1))
  59. data_hash["data"].delete(c)
  60. end
  61. end
  62. hash = {lx: @p[0].value.to_i, ly: @p[1].value.to_i, rx: (@p[0].value.to_i + @p[2].value.to_i - 1), ry: (@p[1].value.to_i + @p[3].value.to_i - 1), url: @p[4].value};
  63. File.write(path, JSON.dump(data_hash))
  64. @a[0].send(JSON.dump(hash))
  65. nil
  66. end
  67. end
  68. def doOverlap(l1x, l1y, r1x, r1y, l2x, l2y, r2x, r2y)
  69. if l1x > r2x || l2x > r1x
  70. return false;
  71. end
  72. if l1y > r2y || l2y > r1y
  73. return false;
  74. end
  75. return true;
  76. end
  77. class Delete < Riddl::Implementation
  78. def response
  79. if cbu = File.read(File.join('data',@r.last,'callback'))
  80. send = { 'operation' => @p[0].value }
  81. case send['operation']
  82. when 'result'
  83. send['target'] = JSON::parse(@p[1].value.read)
  84. end
  85. cbu += '/' unless cbu[-1] == '/'
  86. Typhoeus.put(cbu, body: JSON::generate(send), headers: { 'content-type' => 'application/json'})
  87. end
  88. File.unlink(File.join('data',@r.last,'callback')) rescue nil
  89. File.unlink(File.join('data',@r.last,'style.url')) rescue nil
  90. File.unlink(File.join('data',@r.last,'document.xml')) rescue nil
  91. File.unlink(File.join('data',@r.last,'info.json')) rescue nil
  92. @a[0].send('reset')
  93. nil
  94. end
  95. end
  96. class GetInfo < Riddl::Implementation #{{{
  97. def response
  98. fname = File.join('data',@r[-2],'info.json')
  99. if File.exists? fname
  100. Riddl::Parameter::Complex.new('value','application/json',File.read(fname))
  101. else
  102. @status = 404
  103. end
  104. end
  105. end #}}}
  106. class GetLangs < Riddl::Implementation #{{{
  107. def response
  108. fname = File.join('data',@r[-2],'document.xml')
  109. if File.exists? fname
  110. doc = XML::Smart.open_unprotected(fname)
  111. ndoc = XML::Smart.string('<languages/>')
  112. doc.find('//@lang').each do |e|
  113. ndoc.root.add('language',e.value)
  114. end
  115. Riddl::Parameter::Complex.new('value','text/xml',ndoc.to_s)
  116. else
  117. @status = 404
  118. end
  119. end
  120. end #}}}
  121. class GetStyle < Riddl::Implementation #{{{
  122. def response
  123. fname = File.join('data',@r[-2],'style.url')
  124. if File.exists? fname
  125. Riddl::Parameter::Complex.new('url','text/plain',File.read(fname).strip)
  126. else
  127. @status = 404
  128. end
  129. end
  130. end #}}}
  131. class GetDocument < Riddl::Implementation #{{{
  132. def response
  133. fname = File.join('data',@r[-3],'document.xml')
  134. if File.exists? fname
  135. doc = XML::Smart.open_unprotected(fname)
  136. val = nil
  137. doc.find("//variant[@lang='#{@r[-1]}']").each do |e|
  138. val = e.text
  139. end
  140. if val
  141. Riddl::Parameter::Complex.new('url','text/plain',val.strip)
  142. else
  143. @status = 404
  144. end
  145. else
  146. @status = 404
  147. end
  148. end
  149. end #}}}
  150. class GetButton < Riddl::Implementation #{{{
  151. def response
  152. fname = File.join('data',@r[-3],'document.xml')
  153. if File.exists? fname
  154. doc = XML::Smart.open_unprotected(fname)
  155. val = nil
  156. doc.find("//variant[@lang='#{@r[-1]}']").each do |e|
  157. val = e.attributes['button']
  158. end
  159. if val
  160. Riddl::Parameter::Complex.new('url','text/plain',val.strip)
  161. else
  162. @status = 404
  163. end
  164. else
  165. @status = 404
  166. end
  167. end
  168. end #}}}
  169. class SSE < Riddl::SSEImplementation #{{{
  170. def onopen
  171. signals = @a[0]
  172. signals.add self
  173. send 'started'
  174. end
  175. def onclose
  176. signals = @a[0]
  177. signals.remove self
  178. nil
  179. end
  180. end #}}}
  181. class Signaling # {{{
  182. def initialize
  183. @binding = []
  184. end
  185. def add(binding)
  186. @binding << binding
  187. end
  188. def remove(binding)
  189. @binding.delete(binding)
  190. end
  191. def length
  192. @binding.length
  193. end
  194. def send(value)
  195. @binding.each do |b|
  196. b.send(value)
  197. end
  198. end
  199. end #}}}
  200. server = Riddl::Server.new(File.join(__dir__,'/frames.xml'), :host => 'localhost') do |opts|
  201. accessible_description true
  202. cross_site_xhr true
  203. opts[:signals] = {}
  204. parallel do
  205. loop do
  206. opts[:signals].each do |k,v|
  207. v.send('keepalive')
  208. end
  209. sleep 5
  210. end
  211. end
  212. on resource do
  213. on resource do |r|
  214. idx = r[:r][0]
  215. opts[:signals][idx] ||= Signaling.new
  216. run Get if get
  217. run NewFrame, opts[:signals][idx] if post 'frame'
  218. run Put, opts[:signals][idx] if put 'input'
  219. run Delete, opts[:signals][idx] if delete 'opa'
  220. run Delete, opts[:signals][idx] if delete 'opb'
  221. on resource 'sse' do
  222. run SSE, opts[:signals][idx] if sse
  223. end
  224. on resource 'languages' do
  225. run GetLangs if get
  226. end
  227. on resource 'style.url' do
  228. run GetStyle if get
  229. end
  230. on resource 'info.json' do
  231. run GetInfo if get
  232. end
  233. on resource 'buttons' do
  234. on resource '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*' do
  235. run GetButton if get
  236. end
  237. end
  238. on resource 'documents' do
  239. on resource '[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*' do
  240. run GetDocument if get
  241. end
  242. end
  243. end
  244. end
  245. end.loop!