Module: OBF::Utils

Defined in:
lib/obf/utils.rb

Defined Under Namespace

Classes: Zipper

Class Method Summary collapse

Class Method Details

.as_progress_percent(a, b, &block) ⇒ Object



385
386
387
388
389
390
391
# File 'lib/obf/utils.rb', line 385

def self.as_progress_percent(a, b, &block)
  if Object.const_defined?('Progress') && Progress.respond_to?(:as_percent)
    Progress.as_percent(a, b, &block)
  else
    block.call
  end
end

.build_zip(dest_path = nil, &block) ⇒ Object



368
369
370
371
372
373
374
375
376
377
# File 'lib/obf/utils.rb', line 368

def self.build_zip(dest_path=nil, &block)
  require 'zip'
  
  if !dest_path
    dest_path = OBF::Utils.temp_path(['archive', '.obz'])
  end
  Zip::File.open(dest_path, Zip::File::CREATE) do |zipfile|
    block.call(Zipper.new(zipfile))
  end
end

.fix_color(str, type = 'hex') ⇒ Object



208
209
210
211
212
213
214
215
216
# File 'lib/obf/utils.rb', line 208

def self.fix_color(str, type='hex')
  lookup = str + "::" + type
  @@colors ||= {}
  return @@colors[lookup] if @@colors[lookup]
  path = File.dirname(File.dirname(__FILE__)) + '/tinycolor_convert.js'
  color = `node #{path} "#{str}" #{type}`.strip
  @@colors[lookup] = color
  color
end

.get_url(url) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/obf/utils.rb', line 3

def self.get_url(url)
  return nil unless url
  content_type = nil
  data = nil
  if url.match(/^data:/)
    content_type = url.split(/;/)[0].split(/:/)[1]
    data = Base64.strict_decode64(url.split(/\,/, 2)[1])
  else
    res = Typhoeus.get(URI.escape(url))
    content_type = res.headers['Content-Type']
    data = res.body
  end
  type = MIME::Types[content_type]
  type = type && type[0]
  extension = ""
  if type.respond_to?(:preferred_extension)
    extension = ("." + type.preferred_extension) if type.preferred_extension
  elsif type.respond_to?(:extensions)
    extension = ("." + type.extensions[0]) if type && type.extensions && type.extensions.length > 0
  end
  res = {
    'content_type' => content_type,
    'data' => data,
    'extension' => extension
  }
  res
end

.identify_file(path) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/obf/utils.rb', line 31

def self.identify_file(path)
  name = File.basename(path) rescue nil
  if name.match(/\.obf$/)
    return :obf
  elsif name.match(/\.obz$/)
    return :obz
  elsif name.match(/\.avz$/)
    return :avz
  else
    json = JSON.parse(File.read(path)) rescue nil
    if json
      if json['format'] && json['format'].match(/^open-board-/)
        return :obf
      end
      return :unknown
    end
    
    begin
      plist = CFPropertyList::List.new(:file => path) rescue nil
      plist_data = CFPropertyList.native_types(plist.value) rescue nil
      if plist_data
        if plist_data['$objects'] && plist_data['$objects'].any?{|o| o['$classname'] == 'SYWord' }
          return :sfy
        end
        return :unknown
      end
    rescue CFFormatError => e
    end
    
    xml = Nokogiri::XML(File.open(path)) rescue nil
    if xml && xml.children.length > 0
      if xml.children[0].name == 'sensorygrid'
        return :sgrid
      end
      return :unknown
    end

    begin
      type = nil
      load_zip(path) do |zipper|
        if zipper.glob('manifest.json').length > 0
          json = JSON.parse(zipper.read('manifest.json')) rescue nil
          if json['root'] && json['format'] && json['format'].match(/^open-board-/)
            type = :obz
          end
        end
        if !type && zipper.glob('*.js').length > 0
          json = JSON.parse(zipper.read('*.js')) rescue nil
          if json['locale'] && json['sheets']
            type = :picto4me
          end
        end
      end
      return type if type
    rescue Zip::Error => e
    end
  end
  return :unknown
end

.image_attrs(path, extension = '') ⇒ Object



274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/obf/utils.rb', line 274

def self.image_attrs(path, extension='')
  res = {}
  if path.match(/^data:/)
    res['content_type'] = path.split(/;/)[0].split(/:/)[1]
    raw = Base64.strict_decode64(path.split(/\,/, 2)[1])
    file = Tempfile.new(['file', extension])
    path = file.path
    file.binmode
    file.write raw
    file.close
  else
    is_file = File.exist?(path) rescue false
    if !is_file
      file = Tempfile.new(['file', extension])
      file.binmode
      file.write path
      path = file.path
      file.close
    end
  end
  data = `identify -verbose #{path}`
  data.split(/\n/).each do |line|
    pre, post = line.sub(/^\s+/, '').split(/:\s/, 2)
    if pre == 'Geometry'
      match = post.match(/(\d+)x(\d+)/)
      if match && match[1] && match[2]
        res['width'] = match[1].to_i
        res['height'] = match[2].to_i
      end
    elsif pre == 'Mime type'
      res['content_type'] = post
    end
  end
  if res['content_type'] && res['content_type'].match(/^image\/svg/)
    res['width'] ||= 300
    res['height'] ||= 300
  end
  res
end

.image_base64(url) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/obf/utils.rb', line 97

def self.image_base64(url)
  image = nil
  if url.match(/:\/\//)
    image = get_url(url)
  else
    types = MIME::Types.type_for(url)
    image = {
      'data' => File.read(url),
      'content_type' => types[0] && types[0].to_s
    }
    if !image['content_type']
      attrs = image_attrs(url)
      image['content_type'] ||= attrs['content_type']
    end
  end
  return nil unless image
  str = "data:" + image['content_type']
  str += ";base64," + Base64.strict_encode64(image['data'])
  str
end

.image_raw(url) ⇒ Object



91
92
93
94
95
# File 'lib/obf/utils.rb', line 91

def self.image_raw(url)
  image = get_url(url)
  return nil unless image
  image
end

.load_zip(path, &block) ⇒ Object



360
361
362
363
364
365
366
# File 'lib/obf/utils.rb', line 360

def self.load_zip(path, &block)
  require 'zip'

  Zip::File.open(path) do |zipfile|
    block.call(Zipper.new(zipfile))
  end
end

.obf_shellObject



193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/obf/utils.rb', line 193

def self.obf_shell
  {
    'format' => 'open-board-0.1',
    'license' => {'type' => 'private'},
    'buttons' => [],
    'grid' => {
      'rows' => 0,
      'columns' => 0,
      'order' => [[]]
    },
    'images' => [],
    'sounds' => []
  }
end

.parse_grid(pre_grid) ⇒ Object



256
257
258
259
260
261
262
263
264
265
# File 'lib/obf/utils.rb', line 256

def self.parse_grid(pre_grid)
  pre_grid ||= {}
  grid = {
    'rows' => pre_grid['rows'] || 1,
    'columns' => pre_grid['columns'] || 1,
    'order' => pre_grid['order'] || [[nil]]
  }
  # TODO: parse order better
  grid
end

.parse_license(pre_license) ⇒ Object



243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/obf/utils.rb', line 243

def self.parse_license(pre_license)
  pre_license = {} unless pre_license.is_a?(Hash)
  license = {}
  ['type', 'copyright_notice_url', 'source_url', 'author_name', 'author_url', 'author_email', 'uneditable'].each do |attr|
    license[attr] = pre_license[attr] if pre_license[attr] != nil
  end
  license['type'] ||= 'private'
  license['copyright_notice_url'] ||= license['copyright_notice_link'] if license.key?('copyright_notice_link')
  license['source_url'] ||= license['source_link'] if license.key?('source_link')
  license['author_url'] ||= license['author_link'] if license.key?('author_link')
  license
end

.parse_obf(obj) ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/obf/utils.rb', line 218

def self.parse_obf(obj)
  json = obj
  if obj.is_a?(String)
    json = JSON.parse(obj)
  end
  ['images', 'sounds', 'buttons'].each do |key|
    json["#{key}_hash"] = json[key]
    if json[key].is_a?(Array)
      hash = {}
      json[key].each do |item|
        hash[item['id']] = item
      end
      json["#{key}_hash"] = hash
    elsif json["#{key}_hash"]
      array = []
      json["#{key}_hash"].each do |id, item|
        item['id'] ||= id
        array << item
      end
      json[key] = array
    end
  end
  json
end

.save_image(image, zipper = nil) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/obf/utils.rb', line 118

def self.save_image(image, zipper=nil)
  if image['data']
    if !image['content_type']
      image['content_type'] = image['data'].split(/;/)[0].split(/:/)[1]
    end
  elsif image['path'] && zipper
    image['raw_data'] = zipper.read(image['path'])
    if !image['content_type']
      types = MIME::Types.type_for(image['path'])
      image['content_type'] = types[0] && types[0].to_s
    end
  elsif image['url']
    url_data = get_url(image['url'])
    image['raw_data'] = url_data['data']
    image['content_type'] = url_data['content_type']
  elsif image['symbol']
    # not supported
  end
  type = MIME::Types[image['content_type']]
  type = type && type[0]
  extension = nil
  if type.respond_to?(:preferred_extension)
    extension = type && ("." + type.preferred_extension)
  elsif type.respond_to?(:extensions)
    extension = type && ("." + type.extensions.first)
  end
  file = Tempfile.new(["image_stash", extension.to_s])
  file.binmode
  if image['data']
    str = Base64.strict_decode64(image['data'].split(/\,/, 2)[1])
    file.write str
  elsif image['raw_data']
    file.write image['raw_data']
  else
    file.close
    return nil
  end
  file.close
  if extension && ['image/jpeg', 'image/jpg', 'image/gif'].include?(image['content_type']) && image['width'] && image['width'] < 1000 && image['width'] == image['height']
    # png files need to be converted to make sure they don't have a transparent bg, or
    # else performance takes a huge hit.
    `cp #{file.path} #{file.path}.#{extension}`
    "#{file.path}.#{extension}"
  else
    # TODO: maybe convert to jpg instead of png?
    # see https://github.com/prawnpdf/prawn/issues/324
    # in that case, fill the image with a white background, perhaps?
    `convert #{file.path} -density 1200 -resize 300x300 -background white -gravity center -extent 300x300 #{file.path}.png`
    "#{file.path}.png"
  end
end

.sound_base64(url) ⇒ Object



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/obf/utils.rb', line 176

def self.sound_base64(url)
  sound = nil
  if url.match(/:\/\//)
    sound = get_url(url)
  else
    types = MIME::Types.type_for(url)
    sound = {
      'data' => File.read(url),
      'content_type' => types[0] && types[0].to_s
    }
  end
  return nil unless sound
  str = "data:" + sound['content_type']
  str += ";base64," + Base64.strict_encode64(sound['data'])
  str
end

.sound_raw(url) ⇒ Object



170
171
172
173
174
# File 'lib/obf/utils.rb', line 170

def self.sound_raw(url)
  sound = get_url(url)
  return nil unless sound
  sound
end

.temp_path(*args) ⇒ Object



267
268
269
270
271
272
# File 'lib/obf/utils.rb', line 267

def self.temp_path(*args)
  file = Tempfile.new(*args)
  res = file.path
  file.unlink
  res
end

.update_current_progress(*args) ⇒ Object



379
380
381
382
383
# File 'lib/obf/utils.rb', line 379

def self.update_current_progress(*args)
  if Object.const_defined?('Progress') && Progress.respond_to?(:update_current_progress)
    Progress.update_current_progress(*args)
  end
end