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/README.md +40 -0
- data/Rakefile +8 -0
- data/lib/forma/action.rb +45 -0
- data/lib/forma/config.rb +68 -0
- data/lib/forma/field.rb +451 -0
- data/lib/forma/form.rb +257 -0
- data/lib/forma/helpers.rb +139 -0
- data/lib/forma/html.rb +163 -2
- data/lib/forma/railtie.rb +15 -0
- data/lib/forma/table.rb +115 -0
- data/lib/forma/utils.rb +24 -0
- data/lib/forma/version.rb +1 -1
- data/lib/forma.rb +8 -0
- data/spec/config_spec.rb +20 -0
- data/spec/field_spec.rb +3 -0
- data/spec/form_spec.rb +33 -0
- data/spec/helpers_spec.rb +18 -0
- data/spec/html_spec.rb +26 -0
- data/spec/spec_helper.rb +0 -6
- data/test/field_test.rb +63 -0
- data/test/form_test.rb +16 -0
- data/test/test_helper.rb +3 -0
- data/vendor/assets/images/ff-icons.png +0 -0
- data/vendor/assets/javascripts/forma.js +147 -0
- data/vendor/assets/stylesheets/forma-min.css +41 -1
- data/vendor/assets/stylesheets/forma.css +326 -6
- data/vendor/less/forma.less +138 -8
- metadata +28 -9
- data/.rspec +0 -1
- data/lib/forma/html/attributes.rb +0 -104
- data/lib/forma/html/element.rb +0 -74
- data/spec/html/attributes_spec.rb +0 -54
- data/spec/html/element_spec.rb +0 -21
data/lib/forma/version.rb
CHANGED
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)
|
data/spec/config_spec.rb
ADDED
@@ -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
|
data/spec/field_spec.rb
ADDED
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
data/test/field_test.rb
ADDED
@@ -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
|
data/test/test_helper.rb
ADDED
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
|
-
.
|
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;}
|