shenmegui 0.3.6 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/shenmegui/controls.rb +47 -36
- data/lib/shenmegui/core.rb +60 -30
- data/lib/shenmegui/file_dialog.rb +11 -10
- data/lib/shenmegui/utils.rb +4 -2
- data/static/script.js +36 -19
- data/static/style.css +20 -2
- data/templates/body.erb +1 -1
- data/templates/label.erb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd26f4f5ed85bdf4a68277307384e178cbcc8a98
|
4
|
+
data.tar.gz: ddc3ee9f6d6d8adea90049f8edb5eb919d88518b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 970a1abb56193a2bd09a33ff0b9d7fb2cd03bfbe8d013b186783d4e586c9a40599ff8aaf41be8678aa7807952731d569794045eddb2b1ed589f5fd5b4459ef0f
|
7
|
+
data.tar.gz: cf06ffb82a85f09c917949cae8b1ab1612b33e71f61ddc60502f1a3397d43d9fe65c97cd1c8b8b9da53016d799a17bcbd3cba0ad41b5c4126d7e231e75ddf295
|
data/lib/shenmegui/controls.rb
CHANGED
@@ -5,19 +5,7 @@ module ShenmeGUI
|
|
5
5
|
class Base
|
6
6
|
attr_accessor :id, :properties, :events, :children, :parent
|
7
7
|
|
8
|
-
|
9
|
-
case obj
|
10
|
-
when String
|
11
|
-
HookedString.new(obj, self)
|
12
|
-
when Array
|
13
|
-
HookedArray.new(obj, self)
|
14
|
-
when Hash
|
15
|
-
HookedHash.new(obj, self)
|
16
|
-
else
|
17
|
-
obj
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
8
|
+
# 读取时直接从@properties读取,写入时则调用update_properties这个统一的接口
|
21
9
|
def self.property(*arr)
|
22
10
|
arr.each do |x|
|
23
11
|
define_method(x) do
|
@@ -39,26 +27,29 @@ module ShenmeGUI
|
|
39
27
|
|
40
28
|
end
|
41
29
|
|
30
|
+
# 注意@default_properties都是未上钩子的属性,因为钩子本身需要控件的self,而此时控件还未实例化
|
42
31
|
def self.default(params)
|
43
|
-
@default_properties
|
32
|
+
@default_properties ||= {}
|
33
|
+
@default_properties.merge!(params)
|
44
34
|
end
|
45
35
|
|
46
36
|
def self.default_properties
|
47
37
|
@default_properties
|
48
38
|
end
|
49
39
|
|
40
|
+
# 注册事件及获取事件的lambda,事件lambda存储在events属性里,最后的self是为了支持链式调用
|
50
41
|
available_events = %w{click input dblclick mouseover mouseout blur focus mousedown mouseup change select}.collect(&:to_sym)
|
51
42
|
available_events.each do |x|
|
52
43
|
define_method("on#{x}") do |&block|
|
53
|
-
return events[x] if block.nil?
|
54
|
-
events[x] = lambda &block
|
44
|
+
return @events[x] if block.nil?
|
45
|
+
@events[x] = lambda &block
|
55
46
|
self
|
56
47
|
end
|
57
48
|
end
|
58
49
|
|
59
50
|
def sync
|
60
51
|
data = @properties
|
61
|
-
|
52
|
+
before_sync
|
62
53
|
msg = "sync:#{@id}->#{data.to_json}"
|
63
54
|
ShenmeGUI.socket.send(msg)
|
64
55
|
end
|
@@ -70,17 +61,19 @@ module ShenmeGUI
|
|
70
61
|
|
71
62
|
def update_properties(data)
|
72
63
|
data = Hash[data.keys.collect(&:to_sym).zip(data.values.collect{|x| add_hook(x)})]
|
73
|
-
@properties.
|
64
|
+
@properties.merge!(data)
|
74
65
|
end
|
75
66
|
|
76
67
|
def sync_events
|
77
68
|
data = @events.keys
|
69
|
+
#return if data.empty?
|
78
70
|
msg = "add_event:#{@id}->#{data.to_json}"
|
79
71
|
ShenmeGUI.socket.send(msg)
|
80
72
|
end
|
81
73
|
|
82
74
|
def initialize(params={})
|
83
|
-
@properties =
|
75
|
+
@properties = {}
|
76
|
+
update_properties(self.class.default_properties.dup) if self.class.default_properties
|
84
77
|
update_properties(params)
|
85
78
|
@id = ShenmeGUI.elements.size
|
86
79
|
ShenmeGUI.elements << self
|
@@ -97,11 +90,26 @@ module ShenmeGUI
|
|
97
90
|
template.result(binding)
|
98
91
|
end
|
99
92
|
|
100
|
-
def
|
93
|
+
def before_sync
|
101
94
|
end
|
102
95
|
|
103
|
-
property :width, :height, :font, :
|
96
|
+
property :width, :height, :font, :margin
|
104
97
|
|
98
|
+
private
|
99
|
+
#这里存在重复给某字符串加钩子的情况,真是有够难抓的BUG
|
100
|
+
def add_hook(obj)
|
101
|
+
#p obj.class
|
102
|
+
case obj
|
103
|
+
when String
|
104
|
+
HookedString.new(obj, self, :sync)
|
105
|
+
when Array
|
106
|
+
HookedArray.new(obj, self, :sync)
|
107
|
+
when Hash
|
108
|
+
HookedHash.new(obj, self, :sync)
|
109
|
+
else
|
110
|
+
obj
|
111
|
+
end
|
112
|
+
end
|
105
113
|
end
|
106
114
|
|
107
115
|
class Body < Base
|
@@ -129,11 +137,6 @@ module ShenmeGUI
|
|
129
137
|
class Textline < Base
|
130
138
|
property :text, :selection
|
131
139
|
shortcut :text
|
132
|
-
|
133
|
-
def text=(v)
|
134
|
-
update_properties({text: v, selection: [v.size]*2 })
|
135
|
-
sync
|
136
|
-
end
|
137
140
|
end
|
138
141
|
|
139
142
|
class Textarea < Base
|
@@ -144,11 +147,6 @@ module ShenmeGUI
|
|
144
147
|
def <<(t)
|
145
148
|
text << "\n#{t}"
|
146
149
|
end
|
147
|
-
|
148
|
-
def text=(v)
|
149
|
-
update_properties({text: v, selection: [v.size]*2 })
|
150
|
-
sync
|
151
|
-
end
|
152
150
|
end
|
153
151
|
|
154
152
|
class Stack < Base
|
@@ -172,9 +170,9 @@ module ShenmeGUI
|
|
172
170
|
shortcut :percent
|
173
171
|
default :width=>'100%'
|
174
172
|
|
175
|
-
def
|
176
|
-
|
177
|
-
|
173
|
+
def before_sync
|
174
|
+
@properties[:percent] = 0 if @properties[:percent] < 0
|
175
|
+
@properties[:percent] = 100 if @properties[:percent] > 100
|
178
176
|
end
|
179
177
|
end
|
180
178
|
|
@@ -194,20 +192,33 @@ module ShenmeGUI
|
|
194
192
|
end
|
195
193
|
|
196
194
|
class Table < Base
|
197
|
-
|
195
|
+
#attr_accessor :row_names_enum
|
196
|
+
property :data, :max_column_width, :column_names, :row_names, :row_names_enum
|
198
197
|
shortcut :data
|
199
198
|
default :width=>'100%', :height=>'150px'
|
200
199
|
|
200
|
+
def before_sync
|
201
|
+
row_names_enum = @properties[:row_names_enum]
|
202
|
+
if row_names_enum
|
203
|
+
@properties[:row_names] = []
|
204
|
+
row_names_enum.rewind
|
205
|
+
@properties[:data].size.times do
|
206
|
+
@properties[:row_names] << row_names_enum.next
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
201
211
|
def << row
|
202
212
|
data << row
|
203
213
|
end
|
204
214
|
end
|
205
215
|
|
206
216
|
controls = constants.reject{|x| x==:Base}
|
217
|
+
control_module = self
|
207
218
|
controls.each do |x|
|
208
219
|
ShenmeGUI.singleton_class.instance_eval do
|
209
220
|
define_method "#{x}".downcase do |*arr, &block|
|
210
|
-
el = const_get(
|
221
|
+
el = control_module.const_get(x).new(*arr)
|
211
222
|
@temp_stack.last.children << el unless @temp_stack.empty?
|
212
223
|
el.parent = @temp_stack.last unless @temp_stack.empty?
|
213
224
|
@temp_stack << el
|
data/lib/shenmegui/core.rb
CHANGED
@@ -5,11 +5,21 @@ module ShenmeGUI
|
|
5
5
|
@elements = []
|
6
6
|
@temp_stack = []
|
7
7
|
|
8
|
+
# FakeSocket把send的内容都记录下来,在WebSocket连接建立时会将这些消息读出,转发到ws上
|
9
|
+
class FakeSocket
|
10
|
+
attr_reader :messages
|
11
|
+
def initialize
|
12
|
+
@messages = []
|
13
|
+
end
|
14
|
+
def send(msg)
|
15
|
+
@messages << msg
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
8
19
|
class << self
|
9
|
-
|
10
|
-
attr_reader :this, :elements
|
20
|
+
attr_reader :this, :elements, :socket
|
11
21
|
|
12
|
-
def
|
22
|
+
def handle_message(msg)
|
13
23
|
match_data = msg.match(/(.+?):(\d+)(?:->)?({.+?})?/)
|
14
24
|
command = match_data[1].to_sym
|
15
25
|
id = match_data[2].to_i
|
@@ -27,15 +37,21 @@ module ShenmeGUI
|
|
27
37
|
end
|
28
38
|
target
|
29
39
|
end
|
40
|
+
private :handle_message
|
30
41
|
|
31
42
|
def app(params={}, &block)
|
43
|
+
raise "ShenmeGUI app has been initialized" if @initialized
|
44
|
+
@initialized = true
|
45
|
+
@fake_socket = FakeSocket.new
|
46
|
+
@socket = @fake_socket
|
32
47
|
body(params, &block)
|
33
48
|
#找一个空闲的端口,不太好看
|
34
49
|
temp_server = TCPServer.open('localhost', 0)
|
35
50
|
@port = temp_server.addr[1]
|
36
51
|
temp_server.close
|
52
|
+
@title = params[:title] || 'Application'
|
37
53
|
app_dir = File.expand_path($PROGRAM_NAME).match(/(.+)\/.+/)[1]
|
38
|
-
File.open("#{app_dir}/index.html", 'w'){ |f| f.write @elements[0].render(port: @port) }
|
54
|
+
File.open("#{app_dir}/index.html", 'w'){ |f| f.write @elements[0].render(port: @port, title: @title) }
|
39
55
|
nil
|
40
56
|
end
|
41
57
|
|
@@ -70,41 +86,55 @@ module ShenmeGUI
|
|
70
86
|
end
|
71
87
|
end
|
72
88
|
|
89
|
+
def open_browser
|
90
|
+
app_dir = File.expand_path($PROGRAM_NAME).match(/(.+)\/.+/)[1]
|
91
|
+
index_path = "#{app_dir}/index.html"
|
92
|
+
if Gem.win_platform?
|
93
|
+
`start #{index_path}`
|
94
|
+
elsif Gem.platforms[1].os == 'linux'
|
95
|
+
`xdg-open #{index_path}`
|
96
|
+
elsif Gem.platforms[1].os == 'darwin'
|
97
|
+
`open #{index_path}`
|
98
|
+
end
|
99
|
+
rescue
|
100
|
+
end
|
101
|
+
|
73
102
|
def start!
|
74
|
-
|
75
|
-
EM.run do
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
103
|
+
EM.run do
|
104
|
+
EM::WebSocket.run(:host => "0.0.0.0", :port => @port) do |ws|
|
105
|
+
ws.onopen do
|
106
|
+
puts "WebSocket connection open"
|
107
|
+
# 同时只能有一个连接,而正常连接关闭的时候会把@socket指向FakeSocket,如果建立连接的时候发现@socket是WebSocket,便把连接关掉
|
108
|
+
@socket.close if @socket.respond_to? :close
|
109
|
+
@socket = ws
|
110
|
+
|
111
|
+
class << ws
|
112
|
+
alias :original_send :send
|
113
|
+
def send(msg)
|
114
|
+
puts "Sent: #{msg}"
|
115
|
+
original_send(msg)
|
82
116
|
end
|
83
117
|
end
|
84
118
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
puts "Recieved message: #{msg}"
|
89
|
-
handle msg
|
119
|
+
@elements.each do |e|
|
120
|
+
e.sync_events
|
121
|
+
e.sync
|
90
122
|
end
|
123
|
+
@socket.send(@fake_socket.messages.shift) until @fake_socket.messages.empty?
|
124
|
+
end
|
91
125
|
|
92
|
-
|
126
|
+
ws.onclose do
|
127
|
+
puts "Connection closed"
|
128
|
+
@socket = @fake_socket
|
93
129
|
end
|
130
|
+
|
131
|
+
ws.onmessage do |msg|
|
132
|
+
puts "Recieved: #{msg}"
|
133
|
+
handle_message msg
|
134
|
+
end
|
135
|
+
|
94
136
|
end
|
95
137
|
end
|
96
|
-
begin
|
97
|
-
app_dir = File.expand_path($PROGRAM_NAME).match(/(.+)\/.+/)[1]
|
98
|
-
index_path = "#{app_dir}/index.html"
|
99
|
-
if Gem.win_platform?
|
100
|
-
`start file:///#{index_path}`
|
101
|
-
elsif Gem.platforms[1].os == 'linux'
|
102
|
-
`xdg-open file:///#{index_path}`
|
103
|
-
end
|
104
|
-
rescue
|
105
|
-
end
|
106
|
-
|
107
|
-
ws_thread.join
|
108
138
|
rescue Interrupt
|
109
139
|
puts 'bye~'
|
110
140
|
end
|
@@ -29,17 +29,17 @@ module ShenmeGUI
|
|
29
29
|
int lpfnHook,
|
30
30
|
LPCSTR lpTemplateName
|
31
31
|
EOS
|
32
|
-
extern 'BOOL
|
33
|
-
extern 'BOOL
|
32
|
+
extern 'BOOL GetOpenFileNameW(struct OPENFILENAME*)'
|
33
|
+
extern 'BOOL GetSaveFileNameW(struct OPENFILENAME*)'
|
34
34
|
extern 'HWND GetForegroundWindow()'
|
35
35
|
|
36
36
|
def self.construct_OPENFILENAME(params={})
|
37
|
-
filter = params[:filter] ? "#{params[:filter]}\0#{params[:filter]}\0\0" : 0
|
37
|
+
filter = params[:filter] ? "#{params[:filter]}\0#{params[:filter]}\0\0".encode('UTF-16LE') : 0
|
38
38
|
title = params[:title] || 0
|
39
39
|
flags = 0x00880000
|
40
40
|
flags += 0x00000200 if params[:multi_select]
|
41
41
|
flags += 0x10000000 if params[:show_hidden]
|
42
|
-
initial_path = params[:initial_path] ? params[:initial_path] : 0
|
42
|
+
initial_path = params[:initial_path] ? params[:initial_path].encode('UTF-16LE') : 0
|
43
43
|
|
44
44
|
path = "\0" * 1024
|
45
45
|
ofn = Struct_OPENFILENAME.malloc
|
@@ -69,19 +69,20 @@ module ShenmeGUI
|
|
69
69
|
|
70
70
|
def self.get_open_file_name(params={})
|
71
71
|
ofn, path = construct_OPENFILENAME(params)
|
72
|
-
|
72
|
+
GetOpenFileNameW(ofn)
|
73
73
|
Fiddle.free(ofn.to_i)
|
74
|
-
path = path.split("\0")
|
74
|
+
path = path.force_encoding('UTF-16LE').encode('UTF-8').split("\0")
|
75
75
|
path = path[1..-1].collect{|x| "#{path[0]}\\#{x}"} if path.size > 1
|
76
|
-
path.collect{|x| x.force_encoding('GBK').encode('UTF-8')}
|
77
76
|
path.size > 1 ? path : path[0]
|
78
77
|
end
|
79
78
|
|
80
79
|
def self.get_save_file_name(params={})
|
80
|
+
#禁止保存文件多选
|
81
|
+
params[:multi_select] = false
|
81
82
|
ofn, path = construct_OPENFILENAME(params)
|
82
|
-
|
83
|
+
GetSaveFileNameW(ofn)
|
83
84
|
Fiddle.free(ofn.to_i)
|
84
|
-
path.force_encoding('
|
85
|
+
path.force_encoding('UTF-16LE').encode('UTF-8').split("\0")
|
85
86
|
end
|
86
87
|
|
87
88
|
#avoid error under linux
|
@@ -99,4 +100,4 @@ module ShenmeGUI
|
|
99
100
|
|
100
101
|
end
|
101
102
|
|
102
|
-
#ShenmeGUI::FileDialog.get_open_file_name(initial_path: '
|
103
|
+
#puts ShenmeGUI::FileDialog.get_open_file_name(initial_path: 'D:\\迅雷下载', multi_select: true, filter: '*.中文')
|
data/lib/shenmegui/utils.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# 用在控件的属性上,给Array String Hash加上钩子,监视其对自身的操作,一旦内容有改变立刻请求同步控件属性
|
1
2
|
module ShenmeGUI
|
2
3
|
@unhook_methods = {
|
3
4
|
array: %i{<< []= clear collect! compact! concat delete delete_at delete_if fill flatten! replace insert keep_if map map! pop push reject! replace rotate! select! shift shuffle! slice! sort! sort_by! uniq! unshift},
|
@@ -7,8 +8,9 @@ module ShenmeGUI
|
|
7
8
|
@unhook_methods.each do |k, v|
|
8
9
|
const_set("Hooked#{k.to_s.capitalize}", Class.new(const_get(k.to_s.capitalize)))
|
9
10
|
const_get("Hooked#{k.to_s.capitalize}").class_eval do
|
10
|
-
def initialize(obj, owner)
|
11
|
+
def initialize(obj, owner, callback)
|
11
12
|
@owner = owner
|
13
|
+
@callback = callback
|
12
14
|
super(obj)
|
13
15
|
end
|
14
16
|
|
@@ -16,7 +18,7 @@ module ShenmeGUI
|
|
16
18
|
methods.each do |k, v|
|
17
19
|
define_method(k) do |*arr, &block|
|
18
20
|
result = v.bind(self).call(*arr, &block)
|
19
|
-
@owner.
|
21
|
+
@owner.send(@callback)
|
20
22
|
result
|
21
23
|
end
|
22
24
|
end
|
data/static/script.js
CHANGED
@@ -7,12 +7,17 @@ var websocket =
|
|
7
7
|
console.log("Connected.");
|
8
8
|
};
|
9
9
|
websocket.onmessage = function(evt){
|
10
|
-
console.log(evt.data);
|
10
|
+
console.log("received: " + evt.data);
|
11
11
|
handleMessage(evt.data);
|
12
12
|
};
|
13
13
|
websocket.onclose = function(evt){
|
14
14
|
console.log("Closed.");
|
15
15
|
};
|
16
|
+
websocket.originalSend = websocket.send;
|
17
|
+
websocket.send = function(msg){
|
18
|
+
console.log("sent: " + msg);
|
19
|
+
websocket.originalSend(msg);
|
20
|
+
}
|
16
21
|
return websocket;
|
17
22
|
})();
|
18
23
|
|
@@ -97,7 +102,7 @@ function getId(obj){
|
|
97
102
|
|
98
103
|
var syncHandlers = {
|
99
104
|
base: (function(target, data){
|
100
|
-
var properties = ['width', 'height', 'font', 'margin'
|
105
|
+
var properties = ['width', 'height', 'font', 'margin'];
|
101
106
|
for(var i=0;i<properties.length;i++){
|
102
107
|
if(data[properties[i]]!=undefined) target.style[properties[i]] = data[properties[i]];
|
103
108
|
}
|
@@ -108,27 +113,19 @@ var syncHandlers = {
|
|
108
113
|
}),
|
109
114
|
|
110
115
|
form: (function(target, data){
|
111
|
-
target.getElementsByClassName('title')[0].getElementsByTagName('span')[0].
|
116
|
+
target.getElementsByClassName('title')[0].getElementsByTagName('span')[0].textContent = data.title;
|
112
117
|
}),
|
113
118
|
|
114
119
|
button: (function(target, data){
|
115
|
-
target.
|
120
|
+
target.textContent = data.text;
|
116
121
|
}),
|
117
122
|
|
118
123
|
textline: (function(target, data){
|
119
124
|
target.value = data.text;
|
120
|
-
if(data.selection){
|
121
|
-
target.selectionStart = data.selection[0];
|
122
|
-
target.selectionEnd = data.selection[1];
|
123
|
-
}
|
124
125
|
}),
|
125
126
|
|
126
127
|
textarea: (function(target, data){
|
127
128
|
target.value = data.text;
|
128
|
-
if(data.selection){
|
129
|
-
target.selectionStart = data.selection[0];
|
130
|
-
target.selectionEnd = data.selection[1];
|
131
|
-
}
|
132
129
|
}),
|
133
130
|
|
134
131
|
image: (function(target, data){
|
@@ -156,7 +153,7 @@ var syncHandlers = {
|
|
156
153
|
var label = document.createElement('label');
|
157
154
|
input.type = 'checkbox';
|
158
155
|
input.value = data.options[i];
|
159
|
-
label.
|
156
|
+
label.textContent = data.options[i];
|
160
157
|
option.appendChild(input);
|
161
158
|
option.appendChild(label);
|
162
159
|
target.appendChild(option);
|
@@ -182,7 +179,7 @@ var syncHandlers = {
|
|
182
179
|
input.type="radio";
|
183
180
|
input.value = data.options[i];
|
184
181
|
input.name = "radio";
|
185
|
-
label.
|
182
|
+
label.textContent = data.options[i];
|
186
183
|
option.appendChild(input);
|
187
184
|
option.appendChild(label);
|
188
185
|
target.appendChild(option);
|
@@ -198,25 +195,45 @@ var syncHandlers = {
|
|
198
195
|
for(var i=0;i<data.options.length;i++){
|
199
196
|
var option = document.createElement('option');
|
200
197
|
option.value = data.options[i];
|
201
|
-
option.
|
198
|
+
option.textContent = data.options[i];
|
202
199
|
target.appendChild(option);
|
203
200
|
}
|
204
201
|
}),
|
205
202
|
|
206
203
|
label: (function(target, data){
|
207
|
-
target.
|
204
|
+
target.textContent = data.text;
|
208
205
|
}),
|
209
206
|
|
210
207
|
table: (function(target, data){
|
211
208
|
for(var i=0;i<target.children.length;) target.removeChild(target.children[i]);
|
212
209
|
var tableData = data.data;
|
213
|
-
var columnSize = tableData[0].length;
|
214
210
|
var table = document.createElement('table');
|
211
|
+
var columnNames = data.column_names;
|
212
|
+
var rowNames = data.row_names;
|
213
|
+
if (columnNames) {
|
214
|
+
var tr = document.createElement('tr');
|
215
|
+
if (rowNames) {
|
216
|
+
var th = document.createElement('th');
|
217
|
+
th.textContent = '';
|
218
|
+
tr.appendChild(th);
|
219
|
+
}
|
220
|
+
for(var i=0; i<columnNames.length; i++ ){
|
221
|
+
var th = document.createElement('th');
|
222
|
+
th.textContent = columnNames[i] || '';
|
223
|
+
tr.appendChild(th);
|
224
|
+
}
|
225
|
+
table.appendChild(tr);
|
226
|
+
}
|
215
227
|
for(var i=0; i<tableData.length; i++){
|
216
228
|
var tr = document.createElement('tr');
|
217
|
-
|
229
|
+
if(rowNames){
|
230
|
+
var th = document.createElement('th');
|
231
|
+
th.textContent = rowNames[i] || '';
|
232
|
+
tr.appendChild(th);
|
233
|
+
}
|
234
|
+
for(var j=0; j<tableData[i].length; j++){
|
218
235
|
var td = document.createElement('td');
|
219
|
-
td.
|
236
|
+
td.textContent = tableData[i][j];
|
220
237
|
tr.appendChild(td);
|
221
238
|
}
|
222
239
|
table.appendChild(tr);
|
data/static/style.css
CHANGED
@@ -131,7 +131,8 @@ input[type="radio"]:checked + label:before {
|
|
131
131
|
|
132
132
|
.table {
|
133
133
|
background-color: #808080;
|
134
|
-
overflow: auto;
|
134
|
+
overflow: auto;
|
135
|
+
padding: 0 0 10px 0; }
|
135
136
|
|
136
137
|
table {
|
137
138
|
border-collapse: collapse;
|
@@ -140,6 +141,23 @@ table {
|
|
140
141
|
td {
|
141
142
|
vertical-align: baseline;
|
142
143
|
padding: 3px 15px 3px 3px;
|
143
|
-
border: 1px solid #
|
144
|
+
border: 1px solid #bfbfbf;
|
145
|
+
overflow: auto; }
|
146
|
+
|
147
|
+
th {
|
148
|
+
background-color: #bfbfbf;
|
149
|
+
padding: 3px 0 3px 9px;
|
150
|
+
border: 1px solid #808080;
|
151
|
+
border-left-color: #fff;
|
152
|
+
box-sizing: border-box; }
|
153
|
+
|
154
|
+
tr th:last-child {
|
155
|
+
border-right-color: #bfbfbf; }
|
156
|
+
|
157
|
+
table tr:first-child th {
|
158
|
+
border-top-color: #fff; }
|
159
|
+
|
160
|
+
table tr:last-child th {
|
161
|
+
border-bottom-color: #bfbfbf; }
|
144
162
|
|
145
163
|
/*# sourceMappingURL=style.css.map */
|
data/templates/body.erb
CHANGED
data/templates/label.erb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
<div id="item-<%= id %>" class="label" data-type="label"
|
1
|
+
<div id="item-<%= id %>" class="label" data-type="label"></div>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shenmegui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.4'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- CicholGricenchos
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: em-websocket
|