lanes 0.5.0 → 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/client/fonts/fontawesome-webfont.woff +0 -0
- data/client/fonts/fontawesome-webfont.woff2 +0 -0
- data/client/lanes/Boot.cjsx +1 -6
- data/client/lanes/components/grid/Body.cjsx +14 -1
- data/client/lanes/components/grid/Grid.cjsx +20 -19
- data/client/lanes/components/grid/Toolbar.cjsx +7 -4
- data/client/lanes/components/grid/editors.scss +1 -1
- data/client/lanes/components/grid/styles.scss +16 -5
- data/client/lanes/components/modal/Modal.cjsx +9 -11
- data/client/lanes/components/record-finder/RecordFinder.cjsx +2 -2
- data/client/lanes/components/record-finder/styles.scss +7 -5
- data/client/lanes/components/shared/FormGroup.cjsx +1 -1
- data/client/lanes/components/shared/Icon.cjsx +3 -2
- data/client/lanes/components/shared/Input.cjsx +1 -0
- data/client/lanes/components/shared/PanelHeader.cjsx +8 -0
- data/client/lanes/components/shared/fields.scss +5 -4
- data/client/lanes/components/shared/styles.scss +18 -0
- data/client/lanes/index.js +0 -1
- data/client/lanes/{plugins → lib}/ResizeSensor.js +0 -0
- data/client/lanes/lib/all.js +13 -0
- data/client/lanes/lib/dom.coffee +1 -1
- data/client/lanes/lib/format.coffee +2 -2
- data/client/lanes/lib/index.js.erb +1 -13
- data/client/lanes/lib/loader.coffee +2 -2
- data/client/lanes/lib/utilFunctions.coffee +12 -0
- data/client/lanes/models/AssociationMap.coffee +27 -18
- data/client/lanes/models/Base.coffee +12 -4
- data/client/lanes/models/Collection.coffee +5 -3
- data/client/lanes/models/PubSub.coffee +1 -1
- data/client/lanes/models/Query.coffee +0 -1
- data/client/lanes/models/State.coffee +5 -1
- data/client/lanes/models/Sync.coffee +5 -9
- data/client/lanes/models/query/ArrayResult.coffee +27 -7
- data/client/lanes/models/query/Result.coffee +2 -0
- data/client/lanes/react/Component.coffee +3 -3
- data/client/lanes/react/Viewport.coffee +17 -5
- data/client/lanes/react/mixins/Access.coffee +2 -2
- data/client/lanes/react/mixins/FieldErrors.coffee +3 -3
- data/client/lanes/remote/BaseClasses.coffee +0 -0
- data/client/lanes/remote/api.coffee +8 -0
- data/client/lanes/styles/fonts/_icons.scss +139 -2
- data/client/lanes/styles/fonts/_variables.scss +142 -4
- data/client/lanes/styles/global/mixins.scss +5 -0
- data/client/lanes/styles/global/styles.scss +5 -2
- data/client/lanes/styles/global.scss +1 -0
- data/client/lanes/vendor/base.js.erb +4 -2
- data/client/lanes/vendor/development/calendar.js +65 -65
- data/client/lanes/vendor/development/commons.js +34530 -34719
- data/client/lanes/vendor/development/data.js +30832 -0
- data/client/lanes/vendor/development/helpers.js +26 -26
- data/client/lanes/vendor/development/toggle.js +19 -19
- data/client/lanes/vendor/development/ui.js +22568 -0
- data/client/lanes/vendor/development/widgets.js +362 -362
- data/client/lanes/vendor/production/calendar.js +65 -65
- data/client/lanes/vendor/production/commons.js +34360 -34549
- data/client/lanes/vendor/production/data.js +30829 -0
- data/client/lanes/vendor/production/toggle.js +19 -19
- data/client/lanes/vendor/production/ui.js +22564 -0
- data/client/lanes/vendor/production/widgets.js +362 -362
- data/client/lanes/vendor/{production/base.js → standalone/index.js} +43836 -53602
- data/client/lanes/workspace/Modal.cjsx +1 -1
- data/client/lanes/workspace/styles/header.scss +1 -0
- data/client/lanes/workspace/styles/layout.scss +12 -0
- data/lanes.gemspec +1 -0
- data/lib/lanes/access/authentication_provider.rb +4 -3
- data/lib/lanes/api/coffeescript_processor.rb +9 -3
- data/lib/lanes/api/{controller.rb → controller_base.rb} +3 -35
- data/lib/lanes/api/formatted_reply.rb +2 -2
- data/lib/lanes/api/generic_controller.rb +42 -0
- data/lib/lanes/api/helper_methods.rb +1 -1
- data/lib/lanes/api/null_authentication_provider.rb +3 -0
- data/lib/lanes/api/request_wrapper.rb +32 -18
- data/lib/lanes/api/root.rb +25 -3
- data/lib/lanes/api/routing.rb +41 -22
- data/lib/lanes/api/sprockets_extension.rb +3 -1
- data/lib/lanes/api.rb +2 -1
- data/lib/lanes/configuration.rb +0 -1
- data/lib/lanes/extension/definition.rb +5 -1
- data/lib/lanes/hot_reload_plugin.rb +0 -1
- data/lib/lanes/version.rb +1 -1
- data/npm-build/build +3 -0
- data/npm-build/{base.js → data.js} +16 -16
- data/npm-build/package.json +3 -1
- data/npm-build/standalone.js +4 -0
- data/npm-build/ui.js +8 -0
- data/npm-build/webpack-standalone.config.js +16 -0
- data/npm-build/webpack.config.js +4 -3
- data/templates/config/lanes.rb +3 -3
- metadata +34 -10
- data/client/lanes/lib/noConflict.coffee +0 -15
- data/client/lanes/plugins/index.js +0 -1
- data/client/lanes/vendor/development/base.js +0 -61239
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 423cf2958270fce5f4d1fb05fbba10fd56afbcd8
|
4
|
+
data.tar.gz: e5a90b6559ebbadf4c9eb63833d0aa59e1b4d017
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f24e95dc672cdb9d18f6937fac870a5d216046739771d534291997369ab4488c807c92a7ad82151428b310466970085e69fd6f7c48caffd94f0a5471baa97cc
|
7
|
+
data.tar.gz: f680982d7d8767bfc225b61bfd38a97e6ab11185d426e60a6c3fad7319479d7433f21bc2a2b843d24d00a32445a9e84c49f611e5cf515f06531216237cd497c6
|
Binary file
|
Binary file
|
data/client/lanes/Boot.cjsx
CHANGED
@@ -1,9 +1,4 @@
|
|
1
1
|
Lanes.renderScreenTo = (selector, options) ->
|
2
2
|
Lanes.config.bootstrap(options)
|
3
3
|
document.addEventListener "DOMContentLoaded", ->
|
4
|
-
viewport = new Lanes.React.Viewport(selector
|
5
|
-
Lanes.Models.PubSub.initialize()
|
6
|
-
Lanes.Extensions.fireOnInitialized(viewport)
|
7
|
-
viewport.renderRoot()
|
8
|
-
Lanes.Extensions.fireOnAvailable(viewport)
|
9
|
-
viewport.onBoot()
|
4
|
+
viewport = new Lanes.React.Viewport({selector, options})
|
@@ -1,3 +1,12 @@
|
|
1
|
+
class AllRows extends Lanes.React.BaseComponent
|
2
|
+
render: ->
|
3
|
+
len = @props.length
|
4
|
+
rows = []
|
5
|
+
for i in [0...len - 1] by 1
|
6
|
+
rows.push @props.itemRenderer(i, i)
|
7
|
+
<div>{rows}</div>
|
8
|
+
|
9
|
+
|
1
10
|
class Lanes.Components.Grid.Body extends Lanes.React.BaseComponent
|
2
11
|
|
3
12
|
propTypes:
|
@@ -72,7 +81,10 @@ class Lanes.Components.Grid.Body extends Lanes.React.BaseComponent
|
|
72
81
|
{fields}
|
73
82
|
</div>
|
74
83
|
|
84
|
+
|
75
85
|
render: ->
|
86
|
+
Rows = if @props.renderCompleteResults then AllRows else Lanes.Vendor.List
|
87
|
+
|
76
88
|
<div className={_.classnames('grid-body', 'is-editing': @props.editing)}>
|
77
89
|
|
78
90
|
<LC.NetworkActivityOverlay model={@props.query} />
|
@@ -84,7 +96,8 @@ class Lanes.Components.Grid.Body extends Lanes.React.BaseComponent
|
|
84
96
|
onCancel={@props.onEditCancel}
|
85
97
|
editing={@props.editing} />
|
86
98
|
|
87
|
-
|
99
|
+
|
100
|
+
<Rows
|
88
101
|
useTranslate3d
|
89
102
|
isEditing={!!@props.editing}
|
90
103
|
itemRenderer={@renderRow}
|
@@ -10,23 +10,6 @@
|
|
10
10
|
|
11
11
|
class Lanes.Components.Grid extends Lanes.React.Component
|
12
12
|
|
13
|
-
mixins: [
|
14
|
-
Lanes.React.Mixins.MonitorSize
|
15
|
-
]
|
16
|
-
|
17
|
-
dataObjects:
|
18
|
-
query: 'props'
|
19
|
-
|
20
|
-
bindDataEvents:
|
21
|
-
query: 'load change sort'
|
22
|
-
|
23
|
-
getInitialState: ->
|
24
|
-
{}
|
25
|
-
|
26
|
-
componentWillReceiveProps: (nextProps) ->
|
27
|
-
if nextProps.autoLoadQuery and nextProps.query isnt @props.query
|
28
|
-
nextProps.query.ensureLoaded()
|
29
|
-
|
30
13
|
propTypes:
|
31
14
|
query: React.PropTypes.instanceOf(Lanes.Models.Query).isRequired
|
32
15
|
width: React.PropTypes.number
|
@@ -40,17 +23,35 @@ class Lanes.Components.Grid extends Lanes.React.Component
|
|
40
23
|
onColumnClick: React.PropTypes.func
|
41
24
|
onSelectionChange: React.PropTypes.func
|
42
25
|
autoLoadQuery: React.PropTypes.bool
|
26
|
+
renderCompleteResults: React.PropTypes.bool
|
43
27
|
toolbarChildren: React.PropTypes.oneOfType([
|
44
28
|
React.PropTypes.element,
|
45
29
|
React.PropTypes.arrayOf(React.PropTypes.element)
|
46
30
|
])
|
47
31
|
|
48
|
-
|
49
|
-
|
32
|
+
mixins: [
|
33
|
+
Lanes.React.Mixins.MonitorSize
|
34
|
+
]
|
35
|
+
|
36
|
+
dataObjects:
|
37
|
+
query: 'props'
|
38
|
+
|
39
|
+
bindDataEvents:
|
40
|
+
query: 'load change sort'
|
41
|
+
|
42
|
+
componentWillReceiveProps: (nextProps) ->
|
43
|
+
if nextProps.autoLoadQuery and nextProps.query isnt @props.query
|
44
|
+
nextProps.query.ensureLoaded()
|
50
45
|
|
51
46
|
componentWillMount: ->
|
52
47
|
@query.ensureLoaded() if @props.autoLoadQuery
|
53
48
|
|
49
|
+
getInitialState: ->
|
50
|
+
{}
|
51
|
+
|
52
|
+
getDefaultProps: ->
|
53
|
+
editorProps: {}, autoLoadQuery: true
|
54
|
+
|
54
55
|
onSortChange: (sortInfo) ->
|
55
56
|
for sortConfig in sortInfo
|
56
57
|
sort = @query.fields.at(sortConfig.name).sortBy
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class Lanes.Components.Grid.Toolbar extends Lanes.React.BaseComponent
|
2
2
|
|
3
3
|
propTypes:
|
4
|
-
|
4
|
+
onAddRecord: React.PropTypes.func
|
5
5
|
startEdit: React.PropTypes.func
|
6
6
|
toolbarChildren: React.PropTypes.oneOfType([
|
7
7
|
React.PropTypes.element,
|
@@ -13,16 +13,19 @@ class Lanes.Components.Grid.Toolbar extends Lanes.React.BaseComponent
|
|
13
13
|
@props.startEdit(0)
|
14
14
|
|
15
15
|
AddButton: ->
|
16
|
-
return null unless @props.allowCreate
|
16
|
+
return null unless @props.onAddRecord and @props.allowCreate
|
17
17
|
<BS.Button className="navbar-btn add-row pull-right"
|
18
18
|
onClick={@onAddRecord} bsSize='small'
|
19
19
|
>
|
20
20
|
<LC.Icon type="plus" />Add Row
|
21
21
|
</BS.Button>
|
22
22
|
|
23
|
+
|
24
|
+
shouldDisplay: ->
|
25
|
+
not ( ( false == @props.commands?.isEditing() or not @props.allowCreate ) and not @props.toolbarChildren )
|
26
|
+
|
23
27
|
render: ->
|
24
|
-
|
25
|
-
return null
|
28
|
+
return null unless @shouldDisplay()
|
26
29
|
|
27
30
|
props = _.extend {}, @props,
|
28
31
|
<Lanes.Components.Grid.Toolbar key="toolbar" {...props} />
|
@@ -9,7 +9,6 @@
|
|
9
9
|
flex-direction: column;
|
10
10
|
flex: 1;
|
11
11
|
|
12
|
-
|
13
12
|
// styles shared between header and row
|
14
13
|
.header, .r {
|
15
14
|
display: flex;
|
@@ -17,13 +16,12 @@
|
|
17
16
|
min-height: 40px;
|
18
17
|
display: flex;
|
19
18
|
flex-direction: row;
|
19
|
+
page-break-inside: avoid;
|
20
20
|
.c {
|
21
21
|
padding: $table-cell-padding;
|
22
22
|
line-height: $line-height-base;
|
23
|
-
|
24
23
|
display: flex;
|
25
24
|
flex-direction: row;
|
26
|
-
|
27
25
|
&.center { justify-content: center; }
|
28
26
|
&.right { justify-content: flex-end; }
|
29
27
|
|
@@ -54,6 +52,11 @@
|
|
54
52
|
right: -5px;
|
55
53
|
color: lightgray;
|
56
54
|
}
|
55
|
+
&:not(.asc):not(.desc) {
|
56
|
+
&:after {
|
57
|
+
@include hidden-print;
|
58
|
+
}
|
59
|
+
}
|
57
60
|
&.asc:after {
|
58
61
|
color: gray;
|
59
62
|
content: $fa-var-sort-asc;
|
@@ -65,13 +68,21 @@
|
|
65
68
|
}
|
66
69
|
}
|
67
70
|
}
|
68
|
-
|
71
|
+
.r {
|
72
|
+
margin-right: 10px;
|
73
|
+
/* page-break-inside: avoid; */
|
74
|
+
break-inside: avoid-page;
|
75
|
+
}
|
69
76
|
.grid-body {
|
70
77
|
border-top: 1px solid $table-border-color;
|
71
78
|
overflow: auto;
|
72
79
|
position: relative; // for absolutely positioned row editor
|
73
80
|
flex-grow: 1;
|
74
|
-
height: 1px;
|
81
|
+
height: 1px; // panel needs height to force scroll
|
82
|
+
@media print {
|
83
|
+
overflow: visible;
|
84
|
+
height: initial;
|
85
|
+
}
|
75
86
|
.r:nth-child(odd) {
|
76
87
|
background-color: $table-bg-accent;
|
77
88
|
}
|
@@ -25,15 +25,15 @@ class Lanes.Components.Modal extends Lanes.React.Component
|
|
25
25
|
getInitialState: ->
|
26
26
|
show: false
|
27
27
|
|
28
|
-
onOkButton: -> @state.onOk?(this)
|
29
|
-
onCancelButton: -> @state.onCancel?(this)
|
30
|
-
onButton: (btn) ->
|
28
|
+
onOkButton: (ev) -> @state.onOk?(this, ev)
|
29
|
+
onCancelButton: (ev) -> @state.onCancel?(this, ev)
|
30
|
+
onButton: (ev, btn) ->
|
31
31
|
@selected = btn
|
32
|
-
@_hide() if @state.autoHide
|
33
32
|
if btn.eventKey is 'ok'
|
34
|
-
@onOkButton()
|
33
|
+
@onOkButton(ev)
|
35
34
|
else
|
36
|
-
@onCancelButton()
|
35
|
+
@onCancelButton(ev)
|
36
|
+
@_hide() if @state.autoHide and ev.defaultPrevented isnt true
|
37
37
|
|
38
38
|
componentWillReceiveProps: (nextProps) ->
|
39
39
|
@replaceState(nextProps)
|
@@ -46,9 +46,7 @@ class Lanes.Components.Modal extends Lanes.React.Component
|
|
46
46
|
@context.viewport.modalProps.show = true
|
47
47
|
@setState(show: true)
|
48
48
|
|
49
|
-
hide: ->
|
50
|
-
@_hide()
|
51
|
-
@state.onCancel?()
|
49
|
+
hide: -> @_hide()
|
52
50
|
|
53
51
|
render: ->
|
54
52
|
return null unless @state.show
|
@@ -58,7 +56,7 @@ class Lanes.Components.Modal extends Lanes.React.Component
|
|
58
56
|
button.eventKey ||= (button.key or button.title).toLowerCase()
|
59
57
|
<BS.Button key={button.title}
|
60
58
|
bsStyle={button.style || 'default'} className={name}
|
61
|
-
onClick={_.partial(@onButton, button)}>{button.title}</BS.Button>
|
59
|
+
onClick={_.partial(@onButton, _, button)}>{button.title}</BS.Button>
|
62
60
|
|
63
61
|
cls = _.classnames('lanes-modal', @state.className, @context.uistate?.layout_size)
|
64
62
|
Body = @state.body
|
@@ -68,7 +66,7 @@ class Lanes.Components.Modal extends Lanes.React.Component
|
|
68
66
|
</BS.Modal.Header>
|
69
67
|
|
70
68
|
<BS.Modal.Body style={maxHeight: @context.viewport.height - 250}>
|
71
|
-
<Body modal={@} />
|
69
|
+
<Body ref="body" {...@props} modal={@} />
|
72
70
|
</BS.Modal.Body>
|
73
71
|
|
74
72
|
<BS.Modal.Footer>{buttons}</BS.Modal.Footer>
|
@@ -49,7 +49,7 @@ class Lanes.Components.RecordFinder extends Lanes.React.Component
|
|
49
49
|
|
50
50
|
getValue: (ev) ->
|
51
51
|
value = if @props.parentModel
|
52
|
-
@props.parentModel[@props.associationName][@props.name]
|
52
|
+
@props.parentModel[@props.associationName]?[@props.name]
|
53
53
|
else
|
54
54
|
@props.model[@props.name]
|
55
55
|
value or ''
|
@@ -59,7 +59,7 @@ class Lanes.Components.RecordFinder extends Lanes.React.Component
|
|
59
59
|
# editOnly writable
|
60
60
|
model = @props.parentModel or @props.model
|
61
61
|
|
62
|
-
<BS.InputGroup>
|
62
|
+
<BS.InputGroup className="record-finder">
|
63
63
|
|
64
64
|
<BS.FormControl
|
65
65
|
{...props} {...handlers}
|
@@ -4,13 +4,15 @@
|
|
4
4
|
}
|
5
5
|
|
6
6
|
|
7
|
-
.record-finder-query-string {
|
8
|
-
text-transform: uppercase;
|
9
|
-
}
|
10
|
-
|
11
7
|
.record-finder {
|
12
8
|
.form-control {
|
13
|
-
|
9
|
+
@media print {
|
10
|
+
border-bottom-right-radius: $border-radius-base;
|
11
|
+
border-top-right-radius: $border-radius-base;
|
12
|
+
}
|
13
|
+
}
|
14
|
+
.input-group-btn {
|
15
|
+
@include hidden-print;
|
14
16
|
}
|
15
17
|
}
|
16
18
|
|
@@ -26,7 +26,7 @@ class Lanes.Components.FormGroup extends Lanes.React.Component
|
|
26
26
|
})
|
27
27
|
<BS.Col {...colProps} className={className}>
|
28
28
|
<BS.FormGroup className={valueClassNames}>
|
29
|
-
<
|
29
|
+
<BS.ControlLabel>{@props.label}</BS.ControlLabel>
|
30
30
|
{@props.children}
|
31
31
|
</BS.FormGroup>
|
32
32
|
</BS.Col>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class Lanes.Components.Icon extends Lanes.React.
|
1
|
+
class Lanes.Components.Icon extends Lanes.React.BaseComponent
|
2
2
|
|
3
3
|
propTypes:
|
4
4
|
type: React.PropTypes.string.isRequired
|
@@ -7,7 +7,8 @@ class Lanes.Components.Icon extends Lanes.React.Component
|
|
7
7
|
render: ->
|
8
8
|
|
9
9
|
classes = _.classnames 'icon', "icon-#{@props.type}", @props.className,
|
10
|
-
'
|
10
|
+
'non-printable': @props.noPrint,
|
11
|
+
'with-action' : @props.onClick,
|
11
12
|
"icon-#{@props.size}" : @props.size,
|
12
13
|
'icon-pulse' : @props.animated
|
13
14
|
'flush' : @props.flush
|
@@ -20,20 +20,16 @@ $lanes-field-margin: 6px;
|
|
20
20
|
}
|
21
21
|
|
22
22
|
}
|
23
|
-
|
24
23
|
&.align-right{
|
25
|
-
text-align: right;
|
26
24
|
.control-label, .value, .rw-widget input {text-align: right; }
|
27
25
|
}
|
28
26
|
&.align-center{
|
29
|
-
text-align: center;
|
30
27
|
.control-label, .value, .rw-widget input { text-align: center; }
|
31
28
|
}
|
32
29
|
|
33
30
|
&.select {
|
34
31
|
.form-control-feedback { right: 30px; }
|
35
32
|
}
|
36
|
-
|
37
33
|
.form-group {
|
38
34
|
background-color: rgba(255, 255, 255, 1);
|
39
35
|
transition: background-color 1000ms linear;
|
@@ -49,4 +45,9 @@ $lanes-field-margin: 6px;
|
|
49
45
|
background-color: darken($color-rgba, 20%);
|
50
46
|
}
|
51
47
|
}
|
48
|
+
|
49
|
+
.rw-datetimepicker {
|
50
|
+
&.rw-has-both { padding-right: 0; }
|
51
|
+
.rw-select { @include hidden-print; }
|
52
|
+
}
|
52
53
|
}
|
@@ -44,3 +44,21 @@
|
|
44
44
|
justify-content: center;
|
45
45
|
.progress { width: 100%; }
|
46
46
|
}
|
47
|
+
|
48
|
+
.icon {
|
49
|
+
&.with-action {
|
50
|
+
cursor: pointer;
|
51
|
+
&:hover{ text-shadow: 1px 2px 2px #666666; }
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
.lanes-panel-heading {
|
56
|
+
display: flex;
|
57
|
+
align-items: center;
|
58
|
+
.spacer {
|
59
|
+
flex: 1;
|
60
|
+
}
|
61
|
+
> *:not(.panel-title){
|
62
|
+
margin-left: 0.5rem;
|
63
|
+
}
|
64
|
+
}
|
data/client/lanes/index.js
CHANGED
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
//= require ./namespace
|
2
|
+
//= require ./utilFunctions
|
3
|
+
//= require ./objToParam
|
4
|
+
//= require ./ModuleSupport
|
5
|
+
//= require ./loader
|
6
|
+
//= require ./MakeBaseClass
|
7
|
+
//= require ./el
|
8
|
+
//= require ./dom-polyfills
|
9
|
+
//= require ./dom
|
10
|
+
//= require ./format
|
11
|
+
//= require ./promise_helpers
|
12
|
+
//= require ./results
|
13
|
+
//= require ./ResizeSensor
|
data/client/lanes/lib/dom.coffee
CHANGED
@@ -66,7 +66,7 @@ _.dom = (unknown, query) ->
|
|
66
66
|
throw new TypeError("Selector / DOM node is not present")
|
67
67
|
else if _.isElement(unknown)
|
68
68
|
unknown
|
69
|
-
else if unknown.isReactComponent
|
69
|
+
else if unknown.isReactComponent or unknown.render
|
70
70
|
Lanes.Vendor.ReactDOM.findDOMNode(unknown)
|
71
71
|
else if unknown.nodeType is 9 # body tag
|
72
72
|
unknown
|
@@ -1,9 +1,9 @@
|
|
1
1
|
Lanes.u.format ||= {}
|
2
2
|
|
3
|
-
Lanes.u.format.
|
3
|
+
Lanes.u.format.shortDate = (d) ->
|
4
4
|
_.moment(d).format('L')
|
5
5
|
|
6
|
-
Lanes.u.format.
|
6
|
+
Lanes.u.format.shortDateTime = (d) ->
|
7
7
|
_.moment(d).format('lll')
|
8
8
|
|
9
9
|
Lanes.u.format.currency = (v) ->
|
@@ -1,16 +1,4 @@
|
|
1
|
-
//=
|
2
|
-
//= require ./namespace
|
3
|
-
//= require ./utilFunctions
|
4
|
-
//= require ./objToParam
|
5
|
-
//= require ./ModuleSupport
|
6
|
-
//= require ./loader
|
7
|
-
//= require ./MakeBaseClass
|
8
|
-
//= require ./el
|
9
|
-
//= require ./dom-polyfills
|
10
|
-
//= require ./dom
|
11
|
-
//= require ./format
|
12
|
-
//= require ./promise_helpers
|
13
|
-
//= require ./results
|
1
|
+
//=require ./all
|
14
2
|
<% if Lanes.env.development?
|
15
3
|
require_asset "./development"
|
16
4
|
else
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
GECKO = /Gecko\//.test(navigator.userAgent)
|
4
4
|
|
5
|
-
class
|
5
|
+
class Lanes.lib.AssetLoader
|
6
6
|
|
7
7
|
constructor: (urls, cb) ->
|
8
8
|
finished = 0
|
@@ -91,7 +91,7 @@ Lanes.lib.RequestAssets = (urls...) ->
|
|
91
91
|
for url, i in urls
|
92
92
|
urls[i] = Lanes.config.assets_path_prefix.concat( '/', urls[i] )
|
93
93
|
new _.Promise( (resolve, reject) ->
|
94
|
-
new
|
94
|
+
new Lanes.lib.AssetLoader(urls, (completed) ->
|
95
95
|
failures = _.pick(completed, (status, url) -> !status.success )
|
96
96
|
if _.isEmpty(failures)
|
97
97
|
resolve(completed)
|
@@ -122,6 +122,8 @@ lcDash = (char, match, index) ->
|
|
122
122
|
|
123
123
|
originalTitleize = _.titleize
|
124
124
|
|
125
|
+
|
126
|
+
# some are taken from https://github.com/epeli/underscore.string
|
125
127
|
_.mixin({
|
126
128
|
dasherize: (str) ->
|
127
129
|
_.trim(str).replace(/([A-Z])/g, lcDash).replace(/[-_\s]+/g, '-').toLowerCase()
|
@@ -144,6 +146,16 @@ _.mixin({
|
|
144
146
|
isPromise: (obj) ->
|
145
147
|
!!obj && (_.isObject(obj) || _.isFunction(obj)) && _.isFunction(obj.then)
|
146
148
|
|
149
|
+
classify: (str) ->
|
150
|
+
s = _.toString(str)
|
151
|
+
@capitalize(@camelCase(s.replace(/[\W_]/g, ' ')).replace(/\s/g, ''))
|
152
|
+
|
153
|
+
humanize: (str) ->
|
154
|
+
@capitalize(@trim(@underscored(str).replace(/_id$/, '').replace(/_/g, ' ')))
|
155
|
+
|
156
|
+
underscored: (str) ->
|
157
|
+
return @trim(str).replace(/([a-z\d])([A-Z]+)/g, '$1_$2').replace(/[-\s]+/g, '_').toLowerCase()
|
158
|
+
|
147
159
|
isBlank: (value) ->
|
148
160
|
switch true
|
149
161
|
when _.isDate(value)
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# Note! An AssociationMap is created for each type of Model, and #
|
5
5
|
# is shared between all instances #
|
6
6
|
# ------------------------------------------------------------------ #
|
7
|
-
class Lanes.Models.
|
7
|
+
class Lanes.Models.AssociationMap
|
8
8
|
constructor: (@klass) ->
|
9
9
|
@klass::derived ||= {}
|
10
10
|
@definitions = @klass::associations
|
@@ -45,6 +45,8 @@ class Lanes.Models.AssocationMap
|
|
45
45
|
getOptions: (name, model) ->
|
46
46
|
definition = @definitions[name]
|
47
47
|
options = { parent: model }
|
48
|
+
if definition.inverse
|
49
|
+
options[ definition.inverse.name ] = model
|
48
50
|
if definition.options
|
49
51
|
_.extend(options, Lanes.u.resultsFor(model, definition.options))
|
50
52
|
options
|
@@ -131,27 +133,34 @@ class Lanes.Models.AssocationMap
|
|
131
133
|
continue if not @exists(name) or
|
132
134
|
(_.isEmpty(value) and not @isCreated(model, name))
|
133
135
|
|
134
|
-
|
136
|
+
definition = @definitions[name]
|
135
137
|
|
136
|
-
if
|
137
|
-
association
|
138
|
+
if @isCreated(model, name)
|
139
|
+
association = model[name]
|
140
|
+
# nothing to do if setting to same object
|
141
|
+
continue if value is association
|
138
142
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
else
|
145
|
-
model.set(this.pk(name), value.id, options) if value.id
|
146
|
-
if Lanes.u.isModel(value)
|
147
|
-
@replace(model, name, value)
|
143
|
+
if association.isProxy and Lanes.u.isModel(value) and not value.isProxy
|
144
|
+
association.replaceWithModel(value, association_name: name)
|
145
|
+
else if definition.model
|
146
|
+
if value
|
147
|
+
@_setModel(model, name, value, options, fn_name)
|
148
148
|
else
|
149
|
-
association
|
150
|
-
|
151
|
-
|
152
|
-
model.trigger("change:#{name}", value, {})
|
149
|
+
association.clear()
|
150
|
+
else
|
151
|
+
if value then association[fn_name]( value, options ) else association.clear()
|
153
152
|
else
|
154
|
-
|
153
|
+
@_setModel(model, name, value, options, fn_name)
|
154
|
+
|
155
|
+
_setModel: (model, name, value, options, fn_name) ->
|
156
|
+
model.set(this.pk(name), value.id, options) if value.id
|
157
|
+
|
158
|
+
if Lanes.u.isModel(value)
|
159
|
+
@replace(model, name, value)
|
160
|
+
else
|
161
|
+
model[name][fn_name]( value )
|
162
|
+
if options?.silent isnt true
|
163
|
+
model.trigger("change:#{name}", value, {})
|
155
164
|
|
156
165
|
pk: (name) ->
|
157
166
|
def = @definitions[name]
|
@@ -20,6 +20,9 @@ class BaseModel
|
|
20
20
|
deps: ['invalidAttributes'], fn: ->
|
21
21
|
_.isEmpty @invalidAttributes #_calculateInvalidAttributes()
|
22
22
|
|
23
|
+
hasErrors:
|
24
|
+
deps: ['errors'], fn: -> not _.isEmpty(@errors)
|
25
|
+
|
23
26
|
errorMessage:
|
24
27
|
deps:['errors'], fn: ->
|
25
28
|
if !@errors then ''
|
@@ -198,9 +201,14 @@ class BaseModel
|
|
198
201
|
data = this.getAttributes(props:true, true)
|
199
202
|
else
|
200
203
|
data = this.unsavedAttributes()
|
201
|
-
if this.associations &&
|
202
|
-
|
203
|
-
|
204
|
+
if this.associations && !options.excludeAssociations
|
205
|
+
# empty associations are not included
|
206
|
+
associationData = this.associations.dataForSave(this, options)
|
207
|
+
_.extend(data, associationData)
|
208
|
+
# if submitting associations, we must include our id so they can be updated
|
209
|
+
unless this.isNew() or _.isEmpty(associationData)
|
210
|
+
data[@idAttribute] = this.getId()
|
211
|
+
|
204
212
|
data
|
205
213
|
|
206
214
|
unCacheDerived: (name) ->
|
@@ -255,7 +263,7 @@ class BaseModel
|
|
255
263
|
klass::session['updated_at'] ||= 'date'
|
256
264
|
|
257
265
|
if klass::associations
|
258
|
-
klass::associations = new Lanes.Models.
|
266
|
+
klass::associations = new Lanes.Models.AssociationMap(klass)
|
259
267
|
|
260
268
|
@afterExtended: (klass) ->
|
261
269
|
return if klass::abstractClass
|
@@ -123,7 +123,9 @@ class ModelsCollection
|
|
123
123
|
|
124
124
|
# true if any models have unsaved data
|
125
125
|
isDirty: ->
|
126
|
-
|
126
|
+
!!@find( (model) ->
|
127
|
+
_.result(model, 'isDirty') isnt false
|
128
|
+
)
|
127
129
|
|
128
130
|
url: -> @model::urlRoot()
|
129
131
|
|
@@ -140,8 +142,8 @@ class ModelsCollection
|
|
140
142
|
dataForSave: (options) ->
|
141
143
|
unsaved = []
|
142
144
|
for model in @models
|
143
|
-
if options.saveAll || model.isDirty
|
144
|
-
unsaved.push( model.dataForSave(options) )
|
145
|
+
if options.saveAll || model.isDirty isnt false
|
146
|
+
unsaved.push( model.dataForSave?(options) or model.serialize())
|
145
147
|
unsaved
|
146
148
|
|
147
149
|
mixins:[
|