forma 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/forma/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module Forma
3
- VERSION = "0.0.0"
3
+ VERSION = "0.1.0"
4
4
  end
data/lib/forma.rb CHANGED
@@ -1,4 +1,12 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  require 'active_support/core_ext'
3
3
  require 'forma/version'
4
+ require 'forma/utils'
4
5
  require 'forma/html'
6
+ require 'forma/config'
7
+ require 'forma/helpers'
8
+ require 'forma/action'
9
+ require 'forma/form'
10
+ require 'forma/table'
11
+ require 'forma/field'
12
+ require 'forma/railtie' if defined?(Rails)
@@ -0,0 +1,20 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe 'Number configuration' do
5
+ specify { Forma.config.num.delimiter.should == ',' }
6
+ specify { Forma.config.num.separator.should == '.' }
7
+ end
8
+
9
+ describe 'Date configuration' do
10
+ specify { Forma.config.date.formatter.should == '%d-%b-%Y' }
11
+ end
12
+
13
+ describe 'Texts configuration' do
14
+ specify { Forma.config.texts.empty.should == '(empty)' }
15
+ end
16
+
17
+ describe 'Map configuration' do
18
+ specify { Forma.config.map.default_latitude == 41.711447 }
19
+ specify { Forma.config.map.default_longitude == 44.754514 }
20
+ end
@@ -0,0 +1,3 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+ include Forma
data/spec/form_spec.rb ADDED
@@ -0,0 +1,33 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ include Forma
5
+
6
+ # describe 'simple form' do
7
+ # before(:all) do
8
+ # @form = Form.new(
9
+ # id: 'user-login',
10
+ # title: 'User Login',
11
+ # collapsible: true,
12
+ # collapsed: false,
13
+ # icon: '/assets/user.png',
14
+ # edit: true,
15
+ # url: '/login',
16
+ # method: 'post',
17
+ # selected_tab: 0,
18
+ # tabs: [
19
+ # Tab.new(title: 'Login', icon: '/assets/user.png', col1: Col.new([
20
+ # TextField.new(name: 'username', label: 'Username', required: true),
21
+ # TextField.new(name: 'password', label: 'Password', required: true)
22
+ # ])),
23
+ # Tab.new(title: 'Reset password', icon: '/assets/password.png'),
24
+ # ]
25
+ # )
26
+ # @element = @form.to_html
27
+ # # puts Nokogiri::XML(@element.to_s, &:noblanks).to_xhtml(indent: 2)
28
+ # end
29
+ # specify { @element.tag.should == 'div' }
30
+ # specify { @element.id.should == 'user-login' }
31
+ # specify { @element.klass.should == [ 'ff-form' ] }
32
+ # # TODO: check other elements
33
+ # end
@@ -0,0 +1,18 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+ include Forma::Helpers
4
+
5
+ # describe "forma helper" do
6
+ # before(:all) do
7
+ # @user_password = { username: 'dimitri', password: 'secret', age: 33, email: 'dimakura@gmail.com' }
8
+ # @forma = forma_for(@user_password) do |f|
9
+ # f.text_field :username
10
+ # f.password_field :password
11
+ # f.number_field :age
12
+ # f.email_field :email
13
+ # end
14
+ # # puts Nokogiri::XML(@forma, &:noblanks).to_xhtml(indent: 2)
15
+ # end
16
+ # specify { @forma.should_not be_nil }
17
+ # specify { @forma.should be_a String }
18
+ # end
data/spec/html_spec.rb ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ include Forma::Html
5
+
6
+ describe 'attribute helper method' do
7
+ specify { attr('id', 'forma4').to_s.should == 'id="forma4"' }
8
+ specify { attr('btn').to_s.should == 'class="btn"' }
9
+ specify { attr(['btn', 'btn-primary']).to_s.should == 'class="btn btn-primary"' }
10
+ specify { attr({'font-size' => '10px', 'font-weight' => 'bold'}).to_s.should == 'style="font-size:10px;font-weight:bold"' }
11
+ end
12
+
13
+ describe 'element creation' do
14
+ specify { el('div').to_s.should == '<div></div>' }
15
+ specify { el('div', attrs: { id: 'forma4' }).to_s.should == '<div id="forma4"></div>' }
16
+ specify { el('div', attrs: { id: 'forma4', class: 'main' }).to_s.should == '<div id="forma4" class="main"></div>' }
17
+ specify { el('div', attrs: { id: 'forma4', class: ['main', 'very-important'], style: {'font-size' => '10px'} }).to_s.should == '<div id="forma4" class="main very-important" style="font-size:10px"></div>' }
18
+ specify { el('div', children: [el('div')]).to_s.should == '<div><div></div></div>' }
19
+ end
20
+
21
+ describe 'element id' do
22
+ specify { el('div').id.should be_nil }
23
+ specify { el('div', attrs: { id: 'forma4' }).id.should == 'forma4' }
24
+ specify { el('div', attrs: { class: 'myform1' }).klass.should == [ 'myform1' ] }
25
+ specify { el('div', attrs: { class: ['myform1', 'myform2'] }).klass.should == [ 'myform1', 'myform2' ] }
26
+ end
data/spec/spec_helper.rb CHANGED
@@ -5,9 +5,3 @@ require 'forma'
5
5
  RSpec.configure do |config|
6
6
  config.include(RSpec::Matchers)
7
7
  end
8
-
9
- def compare_object(obj, properties)
10
- properties.each do |k, v|
11
- specify { obj.send(k).should == v }
12
- end
13
- end
@@ -0,0 +1,63 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'test_helper'
3
+
4
+ class FieldTest < Test::Unit::TestCase
5
+ include Forma
6
+
7
+ def test_general_field
8
+ fld = Field.new(model_name: 'user', name: 'first_name', value: 'fake value', label: 'First Name')
9
+ assert_equal 'user', fld.model_name
10
+ assert_equal 'first_name', fld.name
11
+ assert_equal 'models.user.first_name', fld.localization_key
12
+ assert_equal 'First Name', fld.label
13
+ assert_equal 'First Name', fld.localized_label
14
+ assert_equal nil, fld.hint
15
+ assert_equal '', fld.localized_hint
16
+ assert_equal 'fake value', fld.value
17
+ assert_equal 'user_first_name', fld.id
18
+ end
19
+
20
+ def test_text_field
21
+ model = { first_name: 'Dimitri', last_name: 'Kurashvili' }
22
+ fld_first_name = TextField.new(model_name: 'user', name: 'first_name', model: model, hint: "user's first name")
23
+ fld_last_name = TextField.new(model_name: 'user', name: 'last_name', model: model)
24
+ assert_equal 'first_name', fld_first_name.name
25
+ assert_equal "user's first name", fld_first_name.hint
26
+ assert_equal "user's first name", fld_first_name.localized_hint
27
+ assert_equal 'Dimitri', fld_first_name.value
28
+ assert_equal 'last_name', fld_last_name.name
29
+ assert_equal 'user_first_name', fld_first_name.id
30
+ assert_equal 'user[first_name]', fld_first_name.parameter_name
31
+ assert_equal 'user_last_name', fld_last_name.id
32
+ assert_equal 'user[last_name]', fld_last_name.parameter_name
33
+ fld_first_name_ka = TextField.new(name: 'ka', parent: fld_first_name)
34
+ assert_equal 'user_first_name_ka', fld_first_name_ka.id
35
+ assert_equal 'user[first_name_attributes[ka]]', fld_first_name_ka.parameter_name
36
+ end
37
+
38
+ def test_text_field_value_override
39
+ model = { first_name: 'Dimitri', last_name: 'Kurashvili' }
40
+ fld_first_name = TextField.new(model_name: 'user', name: 'first_name', model: model, value: 'other_name')
41
+ assert_equal 'other_name', fld_first_name.value
42
+ fld_first_name.value = nil
43
+ assert_equal 'Dimitri', fld_first_name.value
44
+ end
45
+
46
+ def test_generated_text_field
47
+ model = { first_name: 'Dimitri', last_name: 'Kurashvili' }
48
+ fld_first_name = TextField.new(model_name: 'user', name: 'first_name', model: model)
49
+ el = fld_first_name.edit_element('Dimitri')
50
+ assert_equal 'input', el.tag
51
+ assert_equal 'user_first_name', el.attrs_by_name('id').first.value
52
+ assert_equal 'user[first_name]', el.attrs_by_name('name').first.value
53
+ assert_equal 'text', el.attrs_by_name('type').first.value
54
+ assert_equal 'Dimitri', el.attrs_by_name('value').first.value
55
+ end
56
+
57
+ def test_compex_field
58
+ model = { first_name: 'Dimitri', last_name: 'Kurashvili' }
59
+ fld = ComplexField.new(fields: [ TextField.new(name: 'first_name'), TextField.new(name: 'last_name') ])
60
+ fld.model = model
61
+ assert_equal ['Dimitri', 'Kurashvili'], fld.value
62
+ end
63
+ end
data/test/form_test.rb ADDED
@@ -0,0 +1,16 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'test_helper'
3
+
4
+ class FormTest < Test::Unit::TestCase
5
+ include Forma
6
+
7
+ def test_general_field
8
+ model = { first_name: 'Dimitri', last_name: 'Kurashvili' }
9
+ fld_first_name = TextField.new(model_name: 'user', name: 'first_name', model: model)
10
+ fld_last_name = TextField.new(model_name: 'user', name: 'last_name', model: model)
11
+ frm = Form.new
12
+ frm.add_field(fld_first_name)
13
+ frm.add_field(fld_last_name)
14
+ # puts frm.to_html
15
+ end
16
+ end
@@ -0,0 +1,3 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'test/unit'
3
+ require 'forma'
Binary file
@@ -0,0 +1,147 @@
1
+ (function() {
2
+
3
+ var initilizeCollapsibleElement = function() {
4
+ $('.ff-collapsible').click(function(evt) {
5
+ var activeTitle = $(this);
6
+ var formBody = $(activeTitle.parent().siblings('.ff-collapsible-body')[0]);
7
+ var collapseElement = $(activeTitle.children('.ff-collapse')[0]);
8
+ var isCollapsed = collapseElement.hasClass('ff-collapsed');
9
+ if (isCollapsed) {
10
+ formBody.show();
11
+ collapseElement.removeClass('ff-collapsed');
12
+ } else {
13
+ formBody.hide();
14
+ collapseElement.addClass('ff-collapsed');
15
+ }
16
+ });
17
+ };
18
+
19
+ var initializeTabs = function() {
20
+ $('.ff-tabs-header li').click(function(evnt) {
21
+ var newTab = $(this);
22
+ var oldTab = newTab.siblings('.ff-selected');
23
+ if (oldTab) {
24
+ var newIndex = newTab.parent().children().index(newTab);
25
+ var oldIndex = oldTab.parent().children().index(oldTab);
26
+ var newBody = $($(newTab.parent().parent().children()[1]).children()[newIndex]);
27
+ var oldBody = $($(newTab.parent().parent().children()[1]).children()[oldIndex]);
28
+ oldTab.removeClass("ff-selected");
29
+ oldBody.hide();
30
+ newTab.addClass("ff-selected");
31
+ newBody.show();
32
+ // we need to initialize a map
33
+ // if it was not visible and became
34
+ // visible when the tab was open
35
+ initGoogleMaps();
36
+ }
37
+ });
38
+ };
39
+
40
+ var initializeFormSubmit = function() {
41
+ $('form.ff-wait-on-submit').submit(function(evnt) {
42
+ var btn = $($(this).find('button[type=submit]')[0]);
43
+ btn.html(btn.html() + '...');
44
+ btn.attr('disabled', true);
45
+ return true;
46
+ });
47
+ };
48
+
49
+ var initializeTooltips = function() {
50
+ $('.ff-field-hint').tooltip({ placement: 'right', trigger: 'click' });
51
+ $('.ff-action').tooltip({ placement: 'top' });
52
+ // TODO: any other tooltips?
53
+ };
54
+
55
+ var initializeSelectFields = function() {
56
+ $('.ff-select-link').click(function() {
57
+ var link = $(this);
58
+ var url = link.attr('data-url');
59
+ var height = link.attr('data-height');
60
+ var width = link.attr('data-width');
61
+ var screenLeft = window.screenLeft;
62
+ var screenTop = window.screenTop;
63
+ var browserHeight = window.outerHeight;
64
+ var browserWidth = window.outerWidth;
65
+ var left = parseInt((screenLeft + browserWidth - width) / 2);
66
+ var top = parseInt((screenTop + browserHeight - height) / 2);
67
+ var properties = ['height=', height, ',width=', width, ',left=', left, ',top=', top];
68
+ window.open(url, link.attr('data-id'), properties.join(''));
69
+ });
70
+ };
71
+
72
+ // google map initialization
73
+
74
+ var mapsData = {};
75
+
76
+ var registerGoogleMap = function(id, zoom, center, coordinates, draggable) {
77
+ mapsData[id] = { id: id, zoom: zoom, center: center, coordinates: coordinates, draggable: draggable }
78
+ };
79
+
80
+ var getGoogleMap = function(id) {
81
+ return mapsData[id];
82
+ };
83
+
84
+ var googleMapInitialization = function(data) {
85
+ var id = data['id'];
86
+ if ($('#' + id).is(':visible')) {
87
+ var mapOptions = {
88
+ center: new google.maps.LatLng(data.center.latitude, data.center.longitude),
89
+ zoom: data.zoom,
90
+ mapTypeId: google.maps.MapTypeId.HYBRID //ROADMAP
91
+ };
92
+ var map = new google.maps.Map(document.getElementById(id), mapOptions);
93
+ var markers = [ ];
94
+ for (var i = 0, l = data.coordinates.length; i < l; i++) {
95
+ var markerData = data.coordinates[i];
96
+ var markerPosition = new google.maps.LatLng(markerData.latitude, markerData.longitude);
97
+ var marker = new google.maps.Marker({
98
+ position: markerPosition,
99
+ animation: google.maps.Animation.DROP,
100
+ map: map,
101
+ draggable: data.draggable,
102
+ });
103
+ markers.push(marker);
104
+ google.maps.event.addListener(marker, 'dragend', function() {
105
+ var pos = marker.getPosition();
106
+ $('#' + id + '_latitude').val(pos.lat() + '');
107
+ $('#' + id + '_longitude').val(pos.lng());
108
+ });
109
+ }
110
+ data.map = map;
111
+ data.markers = markers;
112
+ }
113
+ };
114
+
115
+ var initGoogleMaps = function() {
116
+ for (var id in mapsData) {
117
+ var data = mapsData[id];
118
+ if (data && !data.map) {
119
+ googleMapInitialization(data);
120
+ }
121
+ }
122
+ };
123
+
124
+ // prepare function
125
+
126
+ var ready = function() {
127
+ initilizeCollapsibleElement();
128
+ initializeTabs();
129
+ initializeFormSubmit();
130
+ initializeTooltips();
131
+ initGoogleMaps();
132
+ initializeSelectFields();
133
+ };
134
+
135
+ // turbolink initilization!
136
+ // http://railscasts.com/episodes/390-turbolinks?view=asciicast
137
+ $(document).ready(ready);
138
+ $(document).on('page:load', ready);
139
+
140
+ // external API
141
+
142
+ window.forma = {
143
+ registerGoogleMap: registerGoogleMap,
144
+ getGoogleMap: getGoogleMap,
145
+ };
146
+
147
+ })();
@@ -1 +1,41 @@
1
- .ff{font-size:14px;}
1
+ .user-select-none{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;}
2
+ .ff-form,.ff-table{margin-bottom:16px;}.ff-form .ff-action,.ff-table .ff-action{margin-left:4px;}.ff-form .ff-action:first-child,.ff-table .ff-action:first-child{margin-left:0;}
3
+ .ff-form .ff-action img,.ff-table .ff-action img{padding-right:4px;height:16px;width:16px;}
4
+ .ff-form .ff-title,.ff-table .ff-title{position:relative;}.ff-form .ff-title .ff-active-title,.ff-table .ff-title .ff-active-title{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:pointer;display:inline-block;padding:4px 8px;font-weight:bold;font-size:14px;}.ff-form .ff-title .ff-active-title .ff-collapse,.ff-table .ff-title .ff-active-title .ff-collapse{display:inline-block;height:14px;width:14px;margin-right:4px;margin-top:2px;}
5
+ .ff-form .ff-title .ff-active-title img,.ff-table .ff-title .ff-active-title img{height:16px;width:16px;overflow:hidden;margin-right:4px;}
6
+ .ff-form .ff-title .ff-title-actions,.ff-table .ff-title .ff-title-actions{position:absolute;right:0;top:4px;padding:0 8px;}
7
+ .ff-form .ff-collapse,.ff-table .ff-collapse{background:url(/assets/glyphicons-halflings.png) -313px -119px;}
8
+ .ff-form .ff-collapsed.ff-collapse,.ff-table .ff-collapsed.ff-collapse{background:url(/assets/glyphicons-halflings.png) -456px -72px;}
9
+ .ff-form .ff-field-hint,.ff-table .ff-field-hint{background:url(/assets/glyphicons-halflings.png) -456px -72px;}
10
+ .ff-form .ff-tabs,.ff-table .ff-tabs{padding:4px 0;}.ff-form .ff-tabs ul.ff-tabs-header,.ff-table .ff-tabs ul.ff-tabs-header{list-style-type:none;margin:0;position:relative;border-bottom:1px solid #b1b1b1;}.ff-form .ff-tabs ul.ff-tabs-header li,.ff-table .ff-tabs ul.ff-tabs-header li{user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;cursor:pointer;display:inline-block;padding:4px 8px;margin-left:8px;margin-bottom:-1px;border:1px solid transparent;}.ff-form .ff-tabs ul.ff-tabs-header li img,.ff-table .ff-tabs ul.ff-tabs-header li img{margin:0 4px;height:16px;width:16px;}
11
+ .ff-form .ff-tabs ul.ff-tabs-header li.ff-selected,.ff-table .ff-tabs ul.ff-tabs-header li.ff-selected{background:#ffffcc;border:1px solid #b1b1b1;border-bottom:1px solid #ffffcc;}
12
+ .ff-form .ff-tabs .ff-tab-actions,.ff-table .ff-tabs .ff-tab-actions{padding:4px 8px;background:#ffffcc;border:1px solid #b1b1b1;margin-top:-1px;}
13
+ .ff-form .ff-tabs .ff-cols,.ff-table .ff-tabs .ff-cols{margin:4px 0 0 0;position:relative;font-size:13px;}.ff-form .ff-tabs .ff-cols .ff-col-100,.ff-table .ff-tabs .ff-cols .ff-col-100{width:100%;}
14
+ .ff-form .ff-tabs .ff-cols .ff-col-50,.ff-table .ff-tabs .ff-cols .ff-col-50{width:50%;float:left;}
15
+ .ff-form .ff-tabs .ff-cols .ff-col:first-child .ff-col-inner,.ff-table .ff-tabs .ff-cols .ff-col:first-child .ff-col-inner{padding-right:4px;}
16
+ .ff-form .ff-tabs .ff-cols .ff-col:last-child .ff-col-inner,.ff-table .ff-tabs .ff-cols .ff-col:last-child .ff-col-inner{padding-left:4px;}
17
+ .ff-form .ff-tabs .ff-cols .ff-col-inner,.ff-table .ff-tabs .ff-cols .ff-col-inner{padding:0;position:relative;}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field{position:relative;border-bottom:1px solid #b1b1b1;}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field:first-child,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field:first-child{border-top:1px solid #b1b1b1;}
18
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label{display:table-cell;vertical-align:top;position:relative;width:150px;padding:4px 4px 4px 8px;background-color:#c8ffb0;border-right:3px solid transparent;}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label.ff-required,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label.ff-required{border-right:3px solid #cf0000;}
19
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label .ff-field-hint,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-label .ff-field-hint{display:block;height:16px;width:16px;position:absolute;right:0;top:8px;background-position:-96px -96px;}
20
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value{display:table-cell;padding:4px;vertical-align:top;}.ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value .ff-field-actions,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-value .ff-field-actions{display:inline-block;border-left:1px solid #b1b1b1;padding-left:8px;margin-left:8px;}
21
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-before,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-before{padding-right:8px;}
22
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-after,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-field-after{padding-left:8px;}
23
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-complex-part,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-complex-part{display:inline-block;padding-right:8px;}
24
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-field,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-complex-field .ff-field{display:inline-block;border:none;padding-right:8px;}
25
+ .ff-form .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-link,.ff-table .ff-tabs .ff-cols .ff-col-inner .ff-field .ff-select-field .ff-select-link{margin-left:8px;}
26
+ .ff-form .ff-tabs .ff-cols:after,.ff-table .ff-tabs .ff-cols:after{content:".";display:block;clear:both;width:0;height:0;overflow:hidden;}
27
+ .ff-form .ff-tabs input,.ff-table .ff-tabs input{padding:4px;margin:0;font-size:13px;}
28
+ .ff-form .ff-tabs select,.ff-table .ff-tabs select{padding:4px;margin:0;font-size:13px;width:auto;}
29
+ .ff-form .ff-tabs .ff-error input,.ff-table .ff-tabs .ff-error input{border:1px solid red;color:red;background:#fff8f8;}
30
+ .ff-form .ff-form-errors,.ff-table .ff-form-errors{margin:8px;padding:8px;border:1px solid red;background:#fee;color:red;}
31
+ .ff-form ul.ff-form-errors li,.ff-table ul.ff-form-errors li{margin-left:16px;}
32
+ .ff-form .ff-field-errors,.ff-table .ff-field-errors{color:red;padding:4px 0;}
33
+ .ff-form .ff-bottom-actions,.ff-table .ff-bottom-actions{padding:8px;border-top:1px solid #b1b1b1;background:#e0e0e0;}
34
+ .ff-form table.ff-common-table,.ff-table table.ff-common-table{margin-top:8px;width:100%;border-collapse:collapse;}.ff-form table.ff-common-table thead,.ff-table table.ff-common-table thead{text-align:left;border-top:1px solid #3316ff;border-bottom:1px solid #3316ff;background:#c8ffb0;}.ff-form table.ff-common-table thead th,.ff-table table.ff-common-table thead th{font-weight:normal;padding:4px 8px;}
35
+ .ff-form table.ff-common-table thead .ff-field-hint,.ff-table table.ff-common-table thead .ff-field-hint{display:inline-block;height:16px;width:16px;background-position:-96px -96px;}
36
+ .ff-form table.ff-common-table tbody td,.ff-table table.ff-common-table tbody td{padding:4px 8px;border-bottom:1px solid #3316ff;}
37
+ .ff-empty{color:#a0a0a0;}
38
+ .ff-table-empty{padding:32px;text-align:center;background-color:#f6f6f6;border:1px solid #a0a0a0;border-top:none;}
39
+ .ff-map{width:100%;height:100%;overflow:hidden;position:absolute;top:0;left:0;right:0;bottom:0;}.ff-map img{max-width:none;}
40
+ .ff-form .ff-title,.ff-table .ff-title{background-color:#cfc8ff;border-bottom:2px solid #3316ff;border-top:2px solid #3316ff;}
41
+ .tooltip-inner{white-space:pre;max-width:none;}