shenmegui 0.3.6 → 0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|