Added devices and companies models #3
13
.gitignore
vendored
Normal file
13
.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#configuration
|
||||
.env
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
debug
|
||||
.vscode
|
||||
.vs/
|
||||
.DS_Store
|
||||
21
app/views/qor/action.tmpl
Normal file
21
app/views/qor/action.tmpl
Normal file
@@ -0,0 +1,21 @@
|
||||
{{$action := .Result}}
|
||||
|
||||
<div class="qor-page__body qor-page__edit">
|
||||
{{render "shared/flashes"}}
|
||||
{{render "shared/errors"}}
|
||||
|
||||
<div class="qor-form-container" data-toggle="qor-action-slideout">
|
||||
<form action="{{.Context.Request.URL}}" method="POST" enctype="multipart/form-data">
|
||||
<input name="_method" value="PUT" type="hidden">
|
||||
|
||||
{{if $action.Resource}}
|
||||
{{render_form $action.Resource.NewStruct (edit_sections $action.Resource)}}
|
||||
{{end}}
|
||||
|
||||
<div class="qor-form__actions" style="margin-top: 0;margin-bottom:10px;">
|
||||
<button class="mdl-button mdl-button--colored mdl-button--raised mdl-js-button mdl-js-ripple-effect qor-button--save" type="submit">{{t "qor_admin.form.submit" "Submit"}}</button>
|
||||
<a class="mdl-button mdl-button--primary mdl-js-button mdl-js-ripple-effect qor-button--cancel" href="javascript:history.back();">{{t "qor_admin.form.cancel" "Cancel"}}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
12
app/views/qor/actions/9.action.tmpl
Normal file
12
app/views/qor/actions/9.action.tmpl
Normal file
@@ -0,0 +1,12 @@
|
||||
{{$context := .}}
|
||||
{{$result := .Result}}
|
||||
{{$allowed_actions := (allowed_actions $context.Resource.GetActions $context.Action $result)}}
|
||||
{{$resource := .Resource}}
|
||||
|
||||
{{if gt (len $allowed_actions) 0 }}
|
||||
<div class="qor-action-forms" data-toggle="qor.action.bulk">
|
||||
{{range $action := $allowed_actions}}
|
||||
{{render_with "shared/action_item" (to_map "Action" $action "Result" $result "Context" $context "Resource" $resource)}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
3
app/views/qor/actions/header/1.page_title.tmpl
Normal file
3
app/views/qor/actions/header/1.page_title.tmpl
Normal file
@@ -0,0 +1,3 @@
|
||||
<span class="mdl-layout-title">{{page_title}}</span>
|
||||
|
||||
|
||||
4
app/views/qor/actions/header/5.userinfo.tmpl
Normal file
4
app/views/qor/actions/header/5.userinfo.tmpl
Normal file
@@ -0,0 +1,4 @@
|
||||
{{if .CurrentUser}}
|
||||
<div class="mdl-layout-spacer"></div>
|
||||
<button class="mdl-button mdl-navigation mdl-layout--small-screen-only qor-mobile--show-actions">{{t "qor_admin.layout.header.actions" "Actions"}} <i class="material-icons">arrow_drop_down</i></button>
|
||||
{{end}}
|
||||
27
app/views/qor/actions/header/6.searchbar.tmpl
Normal file
27
app/views/qor/actions/header/6.searchbar.tmpl
Normal file
@@ -0,0 +1,27 @@
|
||||
{{if .Resource}}
|
||||
{{if .Resource.SearchHandler}}
|
||||
{{ $keyword := .Context.Request.URL.Query.Get "keyword" }}
|
||||
<form class="qor-search-container ignore-dirtyform" method="GET">
|
||||
{{range $key, $values := .Context.Request.URL.Query}}
|
||||
{{if (and (ne $key "keyword") (ne $key "page"))}}
|
||||
{{range $value := $values}}
|
||||
<input name="{{$key}}" value="{{$value}}" type="hidden">
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
<div class="mdl-textfield mdl-js-textfield mdl-textfield--expandable qor-search">
|
||||
<label class="mdl-button mdl-js-button mdl-button--icon qor-search__label" for="inputSearch">
|
||||
<i class="material-icons">search</i>
|
||||
</label>
|
||||
<div class="mdl-textfield__expandable-holder">
|
||||
<input class="mdl-textfield__input qor-search__input" type="text" id="inputSearch" name="keyword" value="{{ $keyword }}" placeholder="{{t "qor_admin.actions.search_bar_search" "Search"}}">
|
||||
<label class="mdl-textfield__label"></label>
|
||||
</div>
|
||||
<button class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored qor-search__clear" type="button">
|
||||
<i class="material-icons md-18">clear</i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{{end}}
|
||||
{{end}}
|
||||
19
app/views/qor/actions/index/5.scope.tmpl
Normal file
19
app/views/qor/actions/index/5.scope.tmpl
Normal file
@@ -0,0 +1,19 @@
|
||||
{{$scopes := get_scopes}}
|
||||
{{$resource := .Resource}}
|
||||
{{if $scopes}}
|
||||
<div class="qor-actions qor-bottomsheet__filter" data-toggle="qor.filter">
|
||||
{{range $scope := $scopes}}
|
||||
{{if $scope.Group}}
|
||||
<select class="qor-action--select" data-toggle="qor.selector" data-clearable="true" name="scopes" placeholder="{{t (printf "%v.scopes.%v" $resource.ToParam $scope.Group) $scope.Group}}">
|
||||
{{range $s := $scope.Scopes}}
|
||||
<option value="{{$s.Name}}" {{if $s.Active}}selected{{end}}>{{t (printf "%v.scopes.%v.%v" $resource.ToParam $scope.Group $s.Label) $s.Label}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
{{else}}
|
||||
{{range $s := $scope.Scopes}}
|
||||
<a class="qor-action--label {{if $s.Active}}is-active{{end}}" href="{{patch_current_url "scopes" $s.Name}}">{{t (printf "%v.scopes.%v" $resource.ToParam $s.Label) $s.Label}} {{if $s.Active}}<i class="material-icons">clear</i>{{end}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
28
app/views/qor/actions/index/9.action.tmpl
Normal file
28
app/views/qor/actions/index/9.action.tmpl
Normal file
@@ -0,0 +1,28 @@
|
||||
{{$context := .}}
|
||||
{{$result := .Result}}
|
||||
{{$allowed_actions := (allowed_actions $context.Resource.GetActions "batch")}}
|
||||
{{$resource := .Resource}}
|
||||
|
||||
{{if gt (len $allowed_actions) 0 }}
|
||||
<div class="qor-action-bulk-edit" data-toggle="qor.action.bulk">
|
||||
<div class="qor-action-forms" style="display: none;">
|
||||
{{range $action := $allowed_actions}}
|
||||
{{render_with "shared/action_item" (to_map "Action" $action "Result" $result "Context" $context "Resource" $resource "BulkEdit" true)}}
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div class="qor-action-bulk-buttons">
|
||||
<button class="mdl-button mdl-button--colored qor-action--bulk" type="button">{{t "qor_admin.actions.bulk_edit" "Bulk Edit"}}</button>
|
||||
<button class="mdl-button mdl-button--accent qor-action--exit-bulk hidden" type="button">{{t "qor_admin.actions.exit_bulk_edit" "Exit Bulk Edit"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{$collection_actions := (allowed_actions $context.Resource.GetActions "collection")}}
|
||||
{{if gt (len $collection_actions) 0 }}
|
||||
<div class="qor-actions qor-collection-actions" style="float: right">
|
||||
{{range $action := $collection_actions}}
|
||||
{{render_with "shared/action_item" (to_map "Action" $action "Result" $result "Context" $context "Resource" $resource "Multiple" true)}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
41
app/views/qor/actions/index/9.advanced_filter.tmpl
Normal file
41
app/views/qor/actions/index/9.advanced_filter.tmpl
Normal file
@@ -0,0 +1,41 @@
|
||||
{{$filters := get_filters}}
|
||||
{{if $filters}}
|
||||
<div class="qor-actions qor-advanced-filter" data-toggle="qor.advancedsearch">
|
||||
<button class="mdl-button mdl-button--colored qor-advanced-filter__toggle" type="button">
|
||||
{{if has_filter}}
|
||||
<span class="qor-notifications__unread"><i class="material-icons" style="font-size: 1.05em">edit</i></span>
|
||||
{{end}}
|
||||
{{t "qor_admin.filter.advanced_filter" "Advanced Filter"}}
|
||||
</button>
|
||||
|
||||
<div class="qor-advanced-filter__dropdown clearfix" style="display: none;" advanced-search-toggle>
|
||||
<button class="mdl-button mdl-button--icon qor-advanced-filter__close">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
<h3 class="mdl-layout-title">{{t "qor_admin.actions.advanced_filter" "Advanced Filter"}}</h3>
|
||||
{{if len saved_filters}}
|
||||
<div class="qor-advanced-filter__savedfilter">
|
||||
<button class="mdl-button qor-advanced-filter__toggle">{{t "qor_admin.filter.saved_filter" "Saved Filter"}}</button>
|
||||
<ul style="display:none;" advanced-search-toggle>
|
||||
{{range $filter := saved_filters}}
|
||||
<li>
|
||||
<a href="{{$filter.URL}}">{{$filter.Name}}</a>
|
||||
<button class="mdl-button mdl-button--icon qor-advanced-filter__delete" style="display: none;" data-filter-name="{{$filter.Name}}">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<form method="GET">
|
||||
{{range $filter := $filters}}
|
||||
{{render_filter $filter}}
|
||||
{{end}}
|
||||
<button type="submit" class="mdl-button mdl-button--colored mdl-button--raised">{{t "qor_admin.filter.apply" "Apply"}}</button>
|
||||
<button type="button" class="mdl-button mdl-button--colored qor-advanced-filter__save">{{t "qor_admin.filter.save_this_filter" "Save This Filter"}}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
BIN
app/views/qor/assets/fonts/MaterialIcons.woff
Normal file
BIN
app/views/qor/assets/fonts/MaterialIcons.woff
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-Black.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-Black.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-BlackItalic.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-BlackItalic.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-Bold.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-Bold.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-BoldItalic.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-Italic.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-Italic.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-Light.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-Light.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-LightItalic.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-LightItalic.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-Medium.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-Medium.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-MediumItalic.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-Regular.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-Regular.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-Thin.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-Thin.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/Roboto-ThinItalic.ttf
Normal file
BIN
app/views/qor/assets/fonts/Roboto-ThinItalic.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/RobotoCondensed-Bold.ttf
Normal file
BIN
app/views/qor/assets/fonts/RobotoCondensed-Bold.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/RobotoCondensed-BoldItalic.ttf
Normal file
BIN
app/views/qor/assets/fonts/RobotoCondensed-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/RobotoCondensed-Italic.ttf
Normal file
BIN
app/views/qor/assets/fonts/RobotoCondensed-Italic.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/RobotoCondensed-Light.ttf
Normal file
BIN
app/views/qor/assets/fonts/RobotoCondensed-Light.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/RobotoCondensed-LightItalic.ttf
Normal file
BIN
app/views/qor/assets/fonts/RobotoCondensed-LightItalic.ttf
Normal file
Binary file not shown.
BIN
app/views/qor/assets/fonts/RobotoCondensed-Regular.ttf
Normal file
BIN
app/views/qor/assets/fonts/RobotoCondensed-Regular.ttf
Normal file
Binary file not shown.
932
app/views/qor/assets/fonts/codepoints
Normal file
932
app/views/qor/assets/fonts/codepoints
Normal file
@@ -0,0 +1,932 @@
|
||||
3d_rotation e84d
|
||||
ac_unit eb3b
|
||||
access_alarm e190
|
||||
access_alarms e191
|
||||
access_time e192
|
||||
accessibility e84e
|
||||
accessible e914
|
||||
account_balance e84f
|
||||
account_balance_wallet e850
|
||||
account_box e851
|
||||
account_circle e853
|
||||
adb e60e
|
||||
add e145
|
||||
add_a_photo e439
|
||||
add_alarm e193
|
||||
add_alert e003
|
||||
add_box e146
|
||||
add_circle e147
|
||||
add_circle_outline e148
|
||||
add_location e567
|
||||
add_shopping_cart e854
|
||||
add_to_photos e39d
|
||||
add_to_queue e05c
|
||||
adjust e39e
|
||||
airline_seat_flat e630
|
||||
airline_seat_flat_angled e631
|
||||
airline_seat_individual_suite e632
|
||||
airline_seat_legroom_extra e633
|
||||
airline_seat_legroom_normal e634
|
||||
airline_seat_legroom_reduced e635
|
||||
airline_seat_recline_extra e636
|
||||
airline_seat_recline_normal e637
|
||||
airplanemode_active e195
|
||||
airplanemode_inactive e194
|
||||
airplay e055
|
||||
airport_shuttle eb3c
|
||||
alarm e855
|
||||
alarm_add e856
|
||||
alarm_off e857
|
||||
alarm_on e858
|
||||
album e019
|
||||
all_inclusive eb3d
|
||||
all_out e90b
|
||||
android e859
|
||||
announcement e85a
|
||||
apps e5c3
|
||||
archive e149
|
||||
arrow_back e5c4
|
||||
arrow_downward e5db
|
||||
arrow_drop_down e5c5
|
||||
arrow_drop_down_circle e5c6
|
||||
arrow_drop_up e5c7
|
||||
arrow_forward e5c8
|
||||
arrow_upward e5d8
|
||||
art_track e060
|
||||
aspect_ratio e85b
|
||||
assessment e85c
|
||||
assignment e85d
|
||||
assignment_ind e85e
|
||||
assignment_late e85f
|
||||
assignment_return e860
|
||||
assignment_returned e861
|
||||
assignment_turned_in e862
|
||||
assistant e39f
|
||||
assistant_photo e3a0
|
||||
attach_file e226
|
||||
attach_money e227
|
||||
attachment e2bc
|
||||
audiotrack e3a1
|
||||
autorenew e863
|
||||
av_timer e01b
|
||||
backspace e14a
|
||||
backup e864
|
||||
battery_alert e19c
|
||||
battery_charging_full e1a3
|
||||
battery_full e1a4
|
||||
battery_std e1a5
|
||||
battery_unknown e1a6
|
||||
beach_access eb3e
|
||||
beenhere e52d
|
||||
block e14b
|
||||
bluetooth e1a7
|
||||
bluetooth_audio e60f
|
||||
bluetooth_connected e1a8
|
||||
bluetooth_disabled e1a9
|
||||
bluetooth_searching e1aa
|
||||
blur_circular e3a2
|
||||
blur_linear e3a3
|
||||
blur_off e3a4
|
||||
blur_on e3a5
|
||||
book e865
|
||||
bookmark e866
|
||||
bookmark_border e867
|
||||
border_all e228
|
||||
border_bottom e229
|
||||
border_clear e22a
|
||||
border_color e22b
|
||||
border_horizontal e22c
|
||||
border_inner e22d
|
||||
border_left e22e
|
||||
border_outer e22f
|
||||
border_right e230
|
||||
border_style e231
|
||||
border_top e232
|
||||
border_vertical e233
|
||||
branding_watermark e06b
|
||||
brightness_1 e3a6
|
||||
brightness_2 e3a7
|
||||
brightness_3 e3a8
|
||||
brightness_4 e3a9
|
||||
brightness_5 e3aa
|
||||
brightness_6 e3ab
|
||||
brightness_7 e3ac
|
||||
brightness_auto e1ab
|
||||
brightness_high e1ac
|
||||
brightness_low e1ad
|
||||
brightness_medium e1ae
|
||||
broken_image e3ad
|
||||
brush e3ae
|
||||
bubble_chart e6dd
|
||||
bug_report e868
|
||||
build e869
|
||||
burst_mode e43c
|
||||
business e0af
|
||||
business_center eb3f
|
||||
cached e86a
|
||||
cake e7e9
|
||||
call e0b0
|
||||
call_end e0b1
|
||||
call_made e0b2
|
||||
call_merge e0b3
|
||||
call_missed e0b4
|
||||
call_missed_outgoing e0e4
|
||||
call_received e0b5
|
||||
call_split e0b6
|
||||
call_to_action e06c
|
||||
camera e3af
|
||||
camera_alt e3b0
|
||||
camera_enhance e8fc
|
||||
camera_front e3b1
|
||||
camera_rear e3b2
|
||||
camera_roll e3b3
|
||||
cancel e5c9
|
||||
card_giftcard e8f6
|
||||
card_membership e8f7
|
||||
card_travel e8f8
|
||||
casino eb40
|
||||
cast e307
|
||||
cast_connected e308
|
||||
center_focus_strong e3b4
|
||||
center_focus_weak e3b5
|
||||
change_history e86b
|
||||
chat e0b7
|
||||
chat_bubble e0ca
|
||||
chat_bubble_outline e0cb
|
||||
check e5ca
|
||||
check_box e834
|
||||
check_box_outline_blank e835
|
||||
check_circle e86c
|
||||
chevron_left e5cb
|
||||
chevron_right e5cc
|
||||
child_care eb41
|
||||
child_friendly eb42
|
||||
chrome_reader_mode e86d
|
||||
class e86e
|
||||
clear e14c
|
||||
clear_all e0b8
|
||||
close e5cd
|
||||
closed_caption e01c
|
||||
cloud e2bd
|
||||
cloud_circle e2be
|
||||
cloud_done e2bf
|
||||
cloud_download e2c0
|
||||
cloud_off e2c1
|
||||
cloud_queue e2c2
|
||||
cloud_upload e2c3
|
||||
code e86f
|
||||
collections e3b6
|
||||
collections_bookmark e431
|
||||
color_lens e3b7
|
||||
colorize e3b8
|
||||
comment e0b9
|
||||
compare e3b9
|
||||
compare_arrows e915
|
||||
computer e30a
|
||||
confirmation_number e638
|
||||
contact_mail e0d0
|
||||
contact_phone e0cf
|
||||
contacts e0ba
|
||||
content_copy e14d
|
||||
content_cut e14e
|
||||
content_paste e14f
|
||||
control_point e3ba
|
||||
control_point_duplicate e3bb
|
||||
copyright e90c
|
||||
create e150
|
||||
create_new_folder e2cc
|
||||
credit_card e870
|
||||
crop e3be
|
||||
crop_16_9 e3bc
|
||||
crop_3_2 e3bd
|
||||
crop_5_4 e3bf
|
||||
crop_7_5 e3c0
|
||||
crop_din e3c1
|
||||
crop_free e3c2
|
||||
crop_landscape e3c3
|
||||
crop_original e3c4
|
||||
crop_portrait e3c5
|
||||
crop_rotate e437
|
||||
crop_square e3c6
|
||||
dashboard e871
|
||||
data_usage e1af
|
||||
date_range e916
|
||||
dehaze e3c7
|
||||
delete e872
|
||||
delete_forever e92b
|
||||
delete_sweep e16c
|
||||
description e873
|
||||
desktop_mac e30b
|
||||
desktop_windows e30c
|
||||
details e3c8
|
||||
developer_board e30d
|
||||
developer_mode e1b0
|
||||
device_hub e335
|
||||
devices e1b1
|
||||
devices_other e337
|
||||
dialer_sip e0bb
|
||||
dialpad e0bc
|
||||
directions e52e
|
||||
directions_bike e52f
|
||||
directions_boat e532
|
||||
directions_bus e530
|
||||
directions_car e531
|
||||
directions_railway e534
|
||||
directions_run e566
|
||||
directions_subway e533
|
||||
directions_transit e535
|
||||
directions_walk e536
|
||||
disc_full e610
|
||||
dns e875
|
||||
do_not_disturb e612
|
||||
do_not_disturb_alt e611
|
||||
do_not_disturb_off e643
|
||||
do_not_disturb_on e644
|
||||
dock e30e
|
||||
domain e7ee
|
||||
done e876
|
||||
done_all e877
|
||||
donut_large e917
|
||||
donut_small e918
|
||||
drafts e151
|
||||
drag_handle e25d
|
||||
drive_eta e613
|
||||
dvr e1b2
|
||||
edit e3c9
|
||||
edit_location e568
|
||||
eject e8fb
|
||||
email e0be
|
||||
enhanced_encryption e63f
|
||||
equalizer e01d
|
||||
error e000
|
||||
error_outline e001
|
||||
euro_symbol e926
|
||||
ev_station e56d
|
||||
event e878
|
||||
event_available e614
|
||||
event_busy e615
|
||||
event_note e616
|
||||
event_seat e903
|
||||
exit_to_app e879
|
||||
expand_less e5ce
|
||||
expand_more e5cf
|
||||
explicit e01e
|
||||
explore e87a
|
||||
exposure e3ca
|
||||
exposure_neg_1 e3cb
|
||||
exposure_neg_2 e3cc
|
||||
exposure_plus_1 e3cd
|
||||
exposure_plus_2 e3ce
|
||||
exposure_zero e3cf
|
||||
extension e87b
|
||||
face e87c
|
||||
fast_forward e01f
|
||||
fast_rewind e020
|
||||
favorite e87d
|
||||
favorite_border e87e
|
||||
featured_play_list e06d
|
||||
featured_video e06e
|
||||
feedback e87f
|
||||
fiber_dvr e05d
|
||||
fiber_manual_record e061
|
||||
fiber_new e05e
|
||||
fiber_pin e06a
|
||||
fiber_smart_record e062
|
||||
file_download e2c4
|
||||
file_upload e2c6
|
||||
filter e3d3
|
||||
filter_1 e3d0
|
||||
filter_2 e3d1
|
||||
filter_3 e3d2
|
||||
filter_4 e3d4
|
||||
filter_5 e3d5
|
||||
filter_6 e3d6
|
||||
filter_7 e3d7
|
||||
filter_8 e3d8
|
||||
filter_9 e3d9
|
||||
filter_9_plus e3da
|
||||
filter_b_and_w e3db
|
||||
filter_center_focus e3dc
|
||||
filter_drama e3dd
|
||||
filter_frames e3de
|
||||
filter_hdr e3df
|
||||
filter_list e152
|
||||
filter_none e3e0
|
||||
filter_tilt_shift e3e2
|
||||
filter_vintage e3e3
|
||||
find_in_page e880
|
||||
find_replace e881
|
||||
fingerprint e90d
|
||||
first_page e5dc
|
||||
fitness_center eb43
|
||||
flag e153
|
||||
flare e3e4
|
||||
flash_auto e3e5
|
||||
flash_off e3e6
|
||||
flash_on e3e7
|
||||
flight e539
|
||||
flight_land e904
|
||||
flight_takeoff e905
|
||||
flip e3e8
|
||||
flip_to_back e882
|
||||
flip_to_front e883
|
||||
folder e2c7
|
||||
folder_open e2c8
|
||||
folder_shared e2c9
|
||||
folder_special e617
|
||||
font_download e167
|
||||
format_align_center e234
|
||||
format_align_justify e235
|
||||
format_align_left e236
|
||||
format_align_right e237
|
||||
format_bold e238
|
||||
format_clear e239
|
||||
format_color_fill e23a
|
||||
format_color_reset e23b
|
||||
format_color_text e23c
|
||||
format_indent_decrease e23d
|
||||
format_indent_increase e23e
|
||||
format_italic e23f
|
||||
format_line_spacing e240
|
||||
format_list_bulleted e241
|
||||
format_list_numbered e242
|
||||
format_paint e243
|
||||
format_quote e244
|
||||
format_shapes e25e
|
||||
format_size e245
|
||||
format_strikethrough e246
|
||||
format_textdirection_l_to_r e247
|
||||
format_textdirection_r_to_l e248
|
||||
format_underlined e249
|
||||
forum e0bf
|
||||
forward e154
|
||||
forward_10 e056
|
||||
forward_30 e057
|
||||
forward_5 e058
|
||||
free_breakfast eb44
|
||||
fullscreen e5d0
|
||||
fullscreen_exit e5d1
|
||||
functions e24a
|
||||
g_translate e927
|
||||
gamepad e30f
|
||||
games e021
|
||||
gavel e90e
|
||||
gesture e155
|
||||
get_app e884
|
||||
gif e908
|
||||
golf_course eb45
|
||||
gps_fixed e1b3
|
||||
gps_not_fixed e1b4
|
||||
gps_off e1b5
|
||||
grade e885
|
||||
gradient e3e9
|
||||
grain e3ea
|
||||
graphic_eq e1b8
|
||||
grid_off e3eb
|
||||
grid_on e3ec
|
||||
group e7ef
|
||||
group_add e7f0
|
||||
group_work e886
|
||||
hd e052
|
||||
hdr_off e3ed
|
||||
hdr_on e3ee
|
||||
hdr_strong e3f1
|
||||
hdr_weak e3f2
|
||||
headset e310
|
||||
headset_mic e311
|
||||
healing e3f3
|
||||
hearing e023
|
||||
help e887
|
||||
help_outline e8fd
|
||||
high_quality e024
|
||||
highlight e25f
|
||||
highlight_off e888
|
||||
history e889
|
||||
home e88a
|
||||
hot_tub eb46
|
||||
hotel e53a
|
||||
hourglass_empty e88b
|
||||
hourglass_full e88c
|
||||
http e902
|
||||
https e88d
|
||||
image e3f4
|
||||
image_aspect_ratio e3f5
|
||||
import_contacts e0e0
|
||||
import_export e0c3
|
||||
important_devices e912
|
||||
inbox e156
|
||||
indeterminate_check_box e909
|
||||
info e88e
|
||||
info_outline e88f
|
||||
input e890
|
||||
insert_chart e24b
|
||||
insert_comment e24c
|
||||
insert_drive_file e24d
|
||||
insert_emoticon e24e
|
||||
insert_invitation e24f
|
||||
insert_link e250
|
||||
insert_photo e251
|
||||
invert_colors e891
|
||||
invert_colors_off e0c4
|
||||
iso e3f6
|
||||
keyboard e312
|
||||
keyboard_arrow_down e313
|
||||
keyboard_arrow_left e314
|
||||
keyboard_arrow_right e315
|
||||
keyboard_arrow_up e316
|
||||
keyboard_backspace e317
|
||||
keyboard_capslock e318
|
||||
keyboard_hide e31a
|
||||
keyboard_return e31b
|
||||
keyboard_tab e31c
|
||||
keyboard_voice e31d
|
||||
kitchen eb47
|
||||
label e892
|
||||
label_outline e893
|
||||
landscape e3f7
|
||||
language e894
|
||||
laptop e31e
|
||||
laptop_chromebook e31f
|
||||
laptop_mac e320
|
||||
laptop_windows e321
|
||||
last_page e5dd
|
||||
launch e895
|
||||
layers e53b
|
||||
layers_clear e53c
|
||||
leak_add e3f8
|
||||
leak_remove e3f9
|
||||
lens e3fa
|
||||
library_add e02e
|
||||
library_books e02f
|
||||
library_music e030
|
||||
lightbulb_outline e90f
|
||||
line_style e919
|
||||
line_weight e91a
|
||||
linear_scale e260
|
||||
link e157
|
||||
linked_camera e438
|
||||
list e896
|
||||
live_help e0c6
|
||||
live_tv e639
|
||||
local_activity e53f
|
||||
local_airport e53d
|
||||
local_atm e53e
|
||||
local_bar e540
|
||||
local_cafe e541
|
||||
local_car_wash e542
|
||||
local_convenience_store e543
|
||||
local_dining e556
|
||||
local_drink e544
|
||||
local_florist e545
|
||||
local_gas_station e546
|
||||
local_grocery_store e547
|
||||
local_hospital e548
|
||||
local_hotel e549
|
||||
local_laundry_service e54a
|
||||
local_library e54b
|
||||
local_mall e54c
|
||||
local_movies e54d
|
||||
local_offer e54e
|
||||
local_parking e54f
|
||||
local_pharmacy e550
|
||||
local_phone e551
|
||||
local_pizza e552
|
||||
local_play e553
|
||||
local_post_office e554
|
||||
local_printshop e555
|
||||
local_see e557
|
||||
local_shipping e558
|
||||
local_taxi e559
|
||||
location_city e7f1
|
||||
location_disabled e1b6
|
||||
location_off e0c7
|
||||
location_on e0c8
|
||||
location_searching e1b7
|
||||
lock e897
|
||||
lock_open e898
|
||||
lock_outline e899
|
||||
looks e3fc
|
||||
looks_3 e3fb
|
||||
looks_4 e3fd
|
||||
looks_5 e3fe
|
||||
looks_6 e3ff
|
||||
looks_one e400
|
||||
looks_two e401
|
||||
loop e028
|
||||
loupe e402
|
||||
low_priority e16d
|
||||
loyalty e89a
|
||||
mail e158
|
||||
mail_outline e0e1
|
||||
map e55b
|
||||
markunread e159
|
||||
markunread_mailbox e89b
|
||||
memory e322
|
||||
menu e5d2
|
||||
merge_type e252
|
||||
message e0c9
|
||||
mic e029
|
||||
mic_none e02a
|
||||
mic_off e02b
|
||||
mms e618
|
||||
mode_comment e253
|
||||
mode_edit e254
|
||||
monetization_on e263
|
||||
money_off e25c
|
||||
monochrome_photos e403
|
||||
mood e7f2
|
||||
mood_bad e7f3
|
||||
more e619
|
||||
more_horiz e5d3
|
||||
more_vert e5d4
|
||||
motorcycle e91b
|
||||
mouse e323
|
||||
move_to_inbox e168
|
||||
movie e02c
|
||||
movie_creation e404
|
||||
movie_filter e43a
|
||||
multiline_chart e6df
|
||||
music_note e405
|
||||
music_video e063
|
||||
my_location e55c
|
||||
nature e406
|
||||
nature_people e407
|
||||
navigate_before e408
|
||||
navigate_next e409
|
||||
navigation e55d
|
||||
near_me e569
|
||||
network_cell e1b9
|
||||
network_check e640
|
||||
network_locked e61a
|
||||
network_wifi e1ba
|
||||
new_releases e031
|
||||
next_week e16a
|
||||
nfc e1bb
|
||||
no_encryption e641
|
||||
no_sim e0cc
|
||||
not_interested e033
|
||||
note e06f
|
||||
note_add e89c
|
||||
notifications e7f4
|
||||
notifications_active e7f7
|
||||
notifications_none e7f5
|
||||
notifications_off e7f6
|
||||
notifications_paused e7f8
|
||||
offline_pin e90a
|
||||
ondemand_video e63a
|
||||
opacity e91c
|
||||
open_in_browser e89d
|
||||
open_in_new e89e
|
||||
open_with e89f
|
||||
pages e7f9
|
||||
pageview e8a0
|
||||
palette e40a
|
||||
pan_tool e925
|
||||
panorama e40b
|
||||
panorama_fish_eye e40c
|
||||
panorama_horizontal e40d
|
||||
panorama_vertical e40e
|
||||
panorama_wide_angle e40f
|
||||
party_mode e7fa
|
||||
pause e034
|
||||
pause_circle_filled e035
|
||||
pause_circle_outline e036
|
||||
payment e8a1
|
||||
people e7fb
|
||||
people_outline e7fc
|
||||
perm_camera_mic e8a2
|
||||
perm_contact_calendar e8a3
|
||||
perm_data_setting e8a4
|
||||
perm_device_information e8a5
|
||||
perm_identity e8a6
|
||||
perm_media e8a7
|
||||
perm_phone_msg e8a8
|
||||
perm_scan_wifi e8a9
|
||||
person e7fd
|
||||
person_add e7fe
|
||||
person_outline e7ff
|
||||
person_pin e55a
|
||||
person_pin_circle e56a
|
||||
personal_video e63b
|
||||
pets e91d
|
||||
phone e0cd
|
||||
phone_android e324
|
||||
phone_bluetooth_speaker e61b
|
||||
phone_forwarded e61c
|
||||
phone_in_talk e61d
|
||||
phone_iphone e325
|
||||
phone_locked e61e
|
||||
phone_missed e61f
|
||||
phone_paused e620
|
||||
phonelink e326
|
||||
phonelink_erase e0db
|
||||
phonelink_lock e0dc
|
||||
phonelink_off e327
|
||||
phonelink_ring e0dd
|
||||
phonelink_setup e0de
|
||||
photo e410
|
||||
photo_album e411
|
||||
photo_camera e412
|
||||
photo_filter e43b
|
||||
photo_library e413
|
||||
photo_size_select_actual e432
|
||||
photo_size_select_large e433
|
||||
photo_size_select_small e434
|
||||
picture_as_pdf e415
|
||||
picture_in_picture e8aa
|
||||
picture_in_picture_alt e911
|
||||
pie_chart e6c4
|
||||
pie_chart_outlined e6c5
|
||||
pin_drop e55e
|
||||
place e55f
|
||||
play_arrow e037
|
||||
play_circle_filled e038
|
||||
play_circle_outline e039
|
||||
play_for_work e906
|
||||
playlist_add e03b
|
||||
playlist_add_check e065
|
||||
playlist_play e05f
|
||||
plus_one e800
|
||||
poll e801
|
||||
polymer e8ab
|
||||
pool eb48
|
||||
portable_wifi_off e0ce
|
||||
portrait e416
|
||||
power e63c
|
||||
power_input e336
|
||||
power_settings_new e8ac
|
||||
pregnant_woman e91e
|
||||
present_to_all e0df
|
||||
print e8ad
|
||||
priority_high e645
|
||||
public e80b
|
||||
publish e255
|
||||
query_builder e8ae
|
||||
question_answer e8af
|
||||
queue e03c
|
||||
queue_music e03d
|
||||
queue_play_next e066
|
||||
radio e03e
|
||||
radio_button_checked e837
|
||||
radio_button_unchecked e836
|
||||
rate_review e560
|
||||
receipt e8b0
|
||||
recent_actors e03f
|
||||
record_voice_over e91f
|
||||
redeem e8b1
|
||||
redo e15a
|
||||
refresh e5d5
|
||||
remove e15b
|
||||
remove_circle e15c
|
||||
remove_circle_outline e15d
|
||||
remove_from_queue e067
|
||||
remove_red_eye e417
|
||||
remove_shopping_cart e928
|
||||
reorder e8fe
|
||||
repeat e040
|
||||
repeat_one e041
|
||||
replay e042
|
||||
replay_10 e059
|
||||
replay_30 e05a
|
||||
replay_5 e05b
|
||||
reply e15e
|
||||
reply_all e15f
|
||||
report e160
|
||||
report_problem e8b2
|
||||
restaurant e56c
|
||||
restaurant_menu e561
|
||||
restore e8b3
|
||||
restore_page e929
|
||||
ring_volume e0d1
|
||||
room e8b4
|
||||
room_service eb49
|
||||
rotate_90_degrees_ccw e418
|
||||
rotate_left e419
|
||||
rotate_right e41a
|
||||
rounded_corner e920
|
||||
router e328
|
||||
rowing e921
|
||||
rss_feed e0e5
|
||||
rv_hookup e642
|
||||
satellite e562
|
||||
save e161
|
||||
scanner e329
|
||||
schedule e8b5
|
||||
school e80c
|
||||
screen_lock_landscape e1be
|
||||
screen_lock_portrait e1bf
|
||||
screen_lock_rotation e1c0
|
||||
screen_rotation e1c1
|
||||
screen_share e0e2
|
||||
sd_card e623
|
||||
sd_storage e1c2
|
||||
search e8b6
|
||||
security e32a
|
||||
select_all e162
|
||||
send e163
|
||||
sentiment_dissatisfied e811
|
||||
sentiment_neutral e812
|
||||
sentiment_satisfied e813
|
||||
sentiment_very_dissatisfied e814
|
||||
sentiment_very_satisfied e815
|
||||
settings e8b8
|
||||
settings_applications e8b9
|
||||
settings_backup_restore e8ba
|
||||
settings_bluetooth e8bb
|
||||
settings_brightness e8bd
|
||||
settings_cell e8bc
|
||||
settings_ethernet e8be
|
||||
settings_input_antenna e8bf
|
||||
settings_input_component e8c0
|
||||
settings_input_composite e8c1
|
||||
settings_input_hdmi e8c2
|
||||
settings_input_svideo e8c3
|
||||
settings_overscan e8c4
|
||||
settings_phone e8c5
|
||||
settings_power e8c6
|
||||
settings_remote e8c7
|
||||
settings_system_daydream e1c3
|
||||
settings_voice e8c8
|
||||
share e80d
|
||||
shop e8c9
|
||||
shop_two e8ca
|
||||
shopping_basket e8cb
|
||||
shopping_cart e8cc
|
||||
short_text e261
|
||||
show_chart e6e1
|
||||
shuffle e043
|
||||
signal_cellular_4_bar e1c8
|
||||
signal_cellular_connected_no_internet_4_bar e1cd
|
||||
signal_cellular_no_sim e1ce
|
||||
signal_cellular_null e1cf
|
||||
signal_cellular_off e1d0
|
||||
signal_wifi_4_bar e1d8
|
||||
signal_wifi_4_bar_lock e1d9
|
||||
signal_wifi_off e1da
|
||||
sim_card e32b
|
||||
sim_card_alert e624
|
||||
skip_next e044
|
||||
skip_previous e045
|
||||
slideshow e41b
|
||||
slow_motion_video e068
|
||||
smartphone e32c
|
||||
smoke_free eb4a
|
||||
smoking_rooms eb4b
|
||||
sms e625
|
||||
sms_failed e626
|
||||
snooze e046
|
||||
sort e164
|
||||
sort_by_alpha e053
|
||||
spa eb4c
|
||||
space_bar e256
|
||||
speaker e32d
|
||||
speaker_group e32e
|
||||
speaker_notes e8cd
|
||||
speaker_notes_off e92a
|
||||
speaker_phone e0d2
|
||||
spellcheck e8ce
|
||||
star e838
|
||||
star_border e83a
|
||||
star_half e839
|
||||
stars e8d0
|
||||
stay_current_landscape e0d3
|
||||
stay_current_portrait e0d4
|
||||
stay_primary_landscape e0d5
|
||||
stay_primary_portrait e0d6
|
||||
stop e047
|
||||
stop_screen_share e0e3
|
||||
storage e1db
|
||||
store e8d1
|
||||
store_mall_directory e563
|
||||
straighten e41c
|
||||
streetview e56e
|
||||
strikethrough_s e257
|
||||
style e41d
|
||||
subdirectory_arrow_left e5d9
|
||||
subdirectory_arrow_right e5da
|
||||
subject e8d2
|
||||
subscriptions e064
|
||||
subtitles e048
|
||||
subway e56f
|
||||
supervisor_account e8d3
|
||||
surround_sound e049
|
||||
swap_calls e0d7
|
||||
swap_horiz e8d4
|
||||
swap_vert e8d5
|
||||
swap_vertical_circle e8d6
|
||||
switch_camera e41e
|
||||
switch_video e41f
|
||||
sync e627
|
||||
sync_disabled e628
|
||||
sync_problem e629
|
||||
system_update e62a
|
||||
system_update_alt e8d7
|
||||
tab e8d8
|
||||
tab_unselected e8d9
|
||||
tablet e32f
|
||||
tablet_android e330
|
||||
tablet_mac e331
|
||||
tag_faces e420
|
||||
tap_and_play e62b
|
||||
terrain e564
|
||||
text_fields e262
|
||||
text_format e165
|
||||
textsms e0d8
|
||||
texture e421
|
||||
theaters e8da
|
||||
thumb_down e8db
|
||||
thumb_up e8dc
|
||||
thumbs_up_down e8dd
|
||||
time_to_leave e62c
|
||||
timelapse e422
|
||||
timeline e922
|
||||
timer e425
|
||||
timer_10 e423
|
||||
timer_3 e424
|
||||
timer_off e426
|
||||
title e264
|
||||
toc e8de
|
||||
today e8df
|
||||
toll e8e0
|
||||
tonality e427
|
||||
touch_app e913
|
||||
toys e332
|
||||
track_changes e8e1
|
||||
traffic e565
|
||||
train e570
|
||||
tram e571
|
||||
transfer_within_a_station e572
|
||||
transform e428
|
||||
translate e8e2
|
||||
trending_down e8e3
|
||||
trending_flat e8e4
|
||||
trending_up e8e5
|
||||
tune e429
|
||||
turned_in e8e6
|
||||
turned_in_not e8e7
|
||||
tv e333
|
||||
unarchive e169
|
||||
undo e166
|
||||
unfold_less e5d6
|
||||
unfold_more e5d7
|
||||
update e923
|
||||
usb e1e0
|
||||
verified_user e8e8
|
||||
vertical_align_bottom e258
|
||||
vertical_align_center e259
|
||||
vertical_align_top e25a
|
||||
vibration e62d
|
||||
video_call e070
|
||||
video_label e071
|
||||
video_library e04a
|
||||
videocam e04b
|
||||
videocam_off e04c
|
||||
videogame_asset e338
|
||||
view_agenda e8e9
|
||||
view_array e8ea
|
||||
view_carousel e8eb
|
||||
view_column e8ec
|
||||
view_comfy e42a
|
||||
view_compact e42b
|
||||
view_day e8ed
|
||||
view_headline e8ee
|
||||
view_list e8ef
|
||||
view_module e8f0
|
||||
view_quilt e8f1
|
||||
view_stream e8f2
|
||||
view_week e8f3
|
||||
vignette e435
|
||||
visibility e8f4
|
||||
visibility_off e8f5
|
||||
voice_chat e62e
|
||||
voicemail e0d9
|
||||
volume_down e04d
|
||||
volume_mute e04e
|
||||
volume_off e04f
|
||||
volume_up e050
|
||||
vpn_key e0da
|
||||
vpn_lock e62f
|
||||
wallpaper e1bc
|
||||
warning e002
|
||||
watch e334
|
||||
watch_later e924
|
||||
wb_auto e42c
|
||||
wb_cloudy e42d
|
||||
wb_incandescent e42e
|
||||
wb_iridescent e436
|
||||
wb_sunny e430
|
||||
wc e63d
|
||||
web e051
|
||||
web_asset e069
|
||||
weekend e16b
|
||||
whatshot e80e
|
||||
widgets e1bd
|
||||
wifi e63e
|
||||
wifi_lock e1e1
|
||||
wifi_tethering e1e2
|
||||
work e8f9
|
||||
wrap_text e25b
|
||||
youtube_searched_for e8fa
|
||||
zoom_in e8ff
|
||||
zoom_out e900
|
||||
zoom_out_map e56b
|
||||
BIN
app/views/qor/assets/images/bg-transparents.png
Normal file
BIN
app/views/qor/assets/images/bg-transparents.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 970 B |
10
app/views/qor/assets/images/icon_trangle.svg
Normal file
10
app/views/qor/assets/images/icon_trangle.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="12px" height="6px" viewBox="0 0 12 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch Beta 40 (33747) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Shape</title>
|
||||
<desc>Created with Sketch Beta.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<polygon id="Shape" fill="#010101" transform="translate(6.000000, 3.000000) scale(1, -1) translate(-6.000000, -3.000000) " points="0 6 6 0 12 6"></polygon>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 632 B |
BIN
app/views/qor/assets/images/logo.png
Normal file
BIN
app/views/qor/assets/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 327 B |
1
app/views/qor/assets/javascripts/app.js
Normal file
1
app/views/qor/assets/javascripts/app.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";$(function(){$(document).on("click.qor.alert",'[data-dismiss="alert"]',function(){$(this).closest(".qor-alert").removeClass("qor-alert__active")}),setTimeout(function(){$('.qor-alert[data-dismissible="true"]').removeClass("qor-alert__active")},5e3)}),$(function(){$(document).on("click",".qor-dialog--global-search",function(e){e.stopPropagation(),$(e.target).parents(".qor-dialog-content").length||$(e.target).is(".qor-dialog-content")||$(".qor-dialog--global-search").remove()}),$(document).on("click",".qor-global-search--show",function(e){e.preventDefault();var a=$(this).data(),o=window.Mustache.render('<div class="qor-dialog qor-dialog--global-search" tabindex="-1" role="dialog" aria-hidden="true"><div class="qor-dialog-content"><form action=[[actionUrl]]><div class="mdl-textfield mdl-js-textfield" id="global-search-textfield"><input class="mdl-textfield__input ignore-dirtyform" name="keyword" id="globalSearch" value="" type="text" placeholder="" /><label class="mdl-textfield__label" for="globalSearch">[[placeholder]]</label></div></form></div></div>',a);$("body").append(o),window.componentHandler.upgradeElement(document.getElementById("global-search-textfield")),$("#globalSearch").focus()})}),$(function(){var l=[],s="qoradmin_menu_status",e=localStorage.getItem(s);e&&e.length&&(l=e.split(",")),$(".qor-menu-container").on("click","> ul > li > a",function(){var e=$(this),a=e.parent(),o=e.next("ul"),t=a.attr("qor-icon-name");o.length&&(o.hasClass("in")?(l.push(t),a.removeClass("is-expanded"),o.one("transitionend",function(){o.removeClass("collapsing in")}).addClass("collapsing").height(0)):(l=_.without(l,t),a.addClass("is-expanded"),o.one("transitionend",function(){o.removeClass("collapsing")}).addClass("collapsing in").height(o.prop("scrollHeight"))),localStorage.setItem(s,l))}).find("> ul > li > a").each(function(){var e=$(this),a=e.parent(),o=e.next("ul"),t=a.attr("qor-icon-name");o.length&&(o.addClass("collapse"),a.addClass("has-menu"),-1!=l.indexOf(t)?o.height(0):(a.addClass("is-expanded"),o.addClass("in").height(o.prop("scrollHeight"))))});var a=$(".qor-page > .qor-page__header"),o=$(".qor-page > .qor-page__body"),t=a.find(".qor-page-subnav__header").length?96:48;a.length&&(a.height()>t&&o.css("padding-top",a.height()),$(".qor-page").addClass("has-header"),$("header.mdl-layout__header").addClass("has-action"))}),$(function(){$(".qor-mobile--show-actions").on("click",function(){$(".qor-page__header").toggleClass("actions-show")})}),$(function(){var p,m,q=$("body"),f="is-selected",b=function(){return q.hasClass("qor-bottomsheets-open")};function _(e){$("[data-url]").removeClass(f),e&&e.length&&e.addClass(f)}q.qorBottomSheets(),q.qorSlideout(),p=q.data("qor.slideout"),m=q.data("qor.bottomsheets"),$(document).on("click.qor.openUrl","[data-url]",function(e){var a,o=$(this),t=$(e.target),l=o.hasClass("qor-button--new"),s=o.hasClass("qor-button--edit"),n=(o.is(".qor-table tr[data-url]")||o.closest(".qor-js-table").length)&&!o.closest(".qor-slideout").length,r=o.data(),i=r.openType,d=o.parents(".qor-theme-slideout").length,c=o.closest(".qor-slideout").length,h=o.hasClass("qor-action-button")||o.hasClass("qor-action--button");if(e.stopPropagation(),!(o.data("ajax-form")||t.closest(".qor-table--bulking").length||t.closest(".qor-button--actions").length||!t.data("url")&&t.is("a")||n&&b()))if("window"!=i){var u,g;if("new_window"!=i)return h&&(u=$(".qor-js-table tbody").find(".mdl-checkbox__input:checked"),g=[],(a=!!u.length&&(u.each(function(){g.push($(this).closest("tr").data("primary-key"))}),g))&&(r=$.extend({},r,{actionData:a}))),r.$target=t,r.method&&"GET"!=r.method.toUpperCase()?void 0:("bottomsheet"!=i&&!h||"slideout"==i?"slideout"==i||n||l&&!b()||s?"slideout"==i||d?o.hasClass(f)?(p.hide(),_()):(p.open(r),_(o)):window.location.href=r.url:q.hasClass("qor-slideout-open")||l&&b()?m.open(r):d?p.open(r):m.open(r):h&&!a&&o.closest('[data-toggle="qor.action.bulk"]').length&&!c?window.QOR.qorConfirm(r.errorNoItem):m.open(r),!1);window.open(r.url,"_blank")}else window.location.href=r.url})}),$(function(){var l=window.location;$(".qor-search").each(function(){var e=$(this),a=e.find(".qor-search__input"),o=e.find(".qor-search__clear"),t=!!a.val();e.closest(".qor-page__header").addClass("has-search"),$("header.mdl-layout__header").addClass("has-search"),o.on("click",function(){a.val()||t?"?"==l.search.replace(new RegExp(a.attr("name")+"\\=?\\w*"),"")?l.href=l.href.split("?")[0]:l.search=l.search.replace(new RegExp(a.attr("name")+"\\=?\\w*"),""):e.removeClass("is-dirty")})})});
|
||||
13
app/views/qor/assets/javascripts/app/alert.js
Normal file
13
app/views/qor/assets/javascripts/app/alert.js
Normal file
@@ -0,0 +1,13 @@
|
||||
$(function() {
|
||||
'use strict';
|
||||
|
||||
$(document).on('click.qor.alert', '[data-dismiss="alert"]', function() {
|
||||
$(this)
|
||||
.closest('.qor-alert')
|
||||
.removeClass('qor-alert__active');
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
$('.qor-alert[data-dismissible="true"]').removeClass('qor-alert__active');
|
||||
}, 5000);
|
||||
});
|
||||
36
app/views/qor/assets/javascripts/app/global-search.js
Normal file
36
app/views/qor/assets/javascripts/app/global-search.js
Normal file
@@ -0,0 +1,36 @@
|
||||
$(function () {
|
||||
|
||||
'use strict';
|
||||
|
||||
var modal = (
|
||||
'<div class="qor-dialog qor-dialog--global-search" tabindex="-1" role="dialog" aria-hidden="true">' +
|
||||
'<div class="qor-dialog-content">' +
|
||||
'<form action=[[actionUrl]]>' +
|
||||
'<div class="mdl-textfield mdl-js-textfield" id="global-search-textfield">' +
|
||||
'<input class="mdl-textfield__input ignore-dirtyform" name="keyword" id="globalSearch" value="" type="text" placeholder="" />' +
|
||||
'<label class="mdl-textfield__label" for="globalSearch">[[placeholder]]</label>' +
|
||||
'</div>' +
|
||||
'</form>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
$(document).on('click', '.qor-dialog--global-search', function(e){
|
||||
e.stopPropagation();
|
||||
if (!$(e.target).parents('.qor-dialog-content').length && !$(e.target).is('.qor-dialog-content')){
|
||||
$('.qor-dialog--global-search').remove();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '.qor-global-search--show', function(e){
|
||||
e.preventDefault();
|
||||
|
||||
var data = $(this).data();
|
||||
var modalHTML = window.Mustache.render(modal, data);
|
||||
|
||||
$('body').append(modalHTML);
|
||||
window.componentHandler.upgradeElement(document.getElementById('global-search-textfield'));
|
||||
$('#globalSearch').focus();
|
||||
|
||||
});
|
||||
});
|
||||
80
app/views/qor/assets/javascripts/app/menu.js
Normal file
80
app/views/qor/assets/javascripts/app/menu.js
Normal file
@@ -0,0 +1,80 @@
|
||||
$(function() {
|
||||
'use strict';
|
||||
|
||||
let menuDatas = [],
|
||||
storageName = 'qoradmin_menu_status',
|
||||
lastMenuStatus = localStorage.getItem(storageName);
|
||||
|
||||
if (lastMenuStatus && lastMenuStatus.length) {
|
||||
menuDatas = lastMenuStatus.split(',');
|
||||
}
|
||||
|
||||
$('.qor-menu-container')
|
||||
.on('click', '> ul > li > a', function() {
|
||||
let $this = $(this),
|
||||
$li = $this.parent(),
|
||||
$ul = $this.next('ul'),
|
||||
menuName = $li.attr('qor-icon-name');
|
||||
|
||||
if (!$ul.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($ul.hasClass('in')) {
|
||||
menuDatas.push(menuName);
|
||||
|
||||
$li.removeClass('is-expanded');
|
||||
$ul
|
||||
.one('transitionend', function() {
|
||||
$ul.removeClass('collapsing in');
|
||||
})
|
||||
.addClass('collapsing')
|
||||
.height(0);
|
||||
} else {
|
||||
menuDatas = _.without(menuDatas, menuName);
|
||||
|
||||
$li.addClass('is-expanded');
|
||||
$ul
|
||||
.one('transitionend', function() {
|
||||
$ul.removeClass('collapsing');
|
||||
})
|
||||
.addClass('collapsing in')
|
||||
.height($ul.prop('scrollHeight'));
|
||||
}
|
||||
localStorage.setItem(storageName, menuDatas);
|
||||
})
|
||||
.find('> ul > li > a')
|
||||
.each(function() {
|
||||
let $this = $(this),
|
||||
$li = $this.parent(),
|
||||
$ul = $this.next('ul'),
|
||||
menuName = $li.attr('qor-icon-name');
|
||||
|
||||
if (!$ul.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ul.addClass('collapse');
|
||||
$li.addClass('has-menu');
|
||||
|
||||
if (menuDatas.indexOf(menuName) != -1) {
|
||||
$ul.height(0);
|
||||
} else {
|
||||
$li.addClass('is-expanded');
|
||||
$ul.addClass('in').height($ul.prop('scrollHeight'));
|
||||
}
|
||||
});
|
||||
|
||||
let $pageHeader = $('.qor-page > .qor-page__header'),
|
||||
$pageBody = $('.qor-page > .qor-page__body'),
|
||||
triggerHeight = $pageHeader.find('.qor-page-subnav__header').length ? 96 : 48;
|
||||
|
||||
if ($pageHeader.length) {
|
||||
if ($pageHeader.height() > triggerHeight) {
|
||||
$pageBody.css('padding-top', $pageHeader.height());
|
||||
}
|
||||
|
||||
$('.qor-page').addClass('has-header');
|
||||
$('header.mdl-layout__header').addClass('has-action');
|
||||
}
|
||||
});
|
||||
5
app/views/qor/assets/javascripts/app/mobile.js
Normal file
5
app/views/qor/assets/javascripts/app/mobile.js
Normal file
@@ -0,0 +1,5 @@
|
||||
$(function () {
|
||||
$('.qor-mobile--show-actions').on('click', function () {
|
||||
$('.qor-page__header').toggleClass('actions-show');
|
||||
});
|
||||
});
|
||||
162
app/views/qor/assets/javascripts/app/open-url.js
Normal file
162
app/views/qor/assets/javascripts/app/open-url.js
Normal file
@@ -0,0 +1,162 @@
|
||||
$(function() {
|
||||
"use strict";
|
||||
|
||||
let $body = $("body"),
|
||||
Slideout,
|
||||
BottomSheets,
|
||||
CLASS_IS_SELECTED = "is-selected",
|
||||
isSlideoutOpened = function() {
|
||||
return $body.hasClass("qor-slideout-open");
|
||||
},
|
||||
isBottomsheetsOpened = function() {
|
||||
return $body.hasClass("qor-bottomsheets-open");
|
||||
};
|
||||
|
||||
$body.qorBottomSheets();
|
||||
$body.qorSlideout();
|
||||
|
||||
Slideout = $body.data("qor.slideout");
|
||||
BottomSheets = $body.data("qor.bottomsheets");
|
||||
|
||||
function toggleSelectedCss(ele) {
|
||||
$("[data-url]").removeClass(CLASS_IS_SELECTED);
|
||||
ele && ele.length && ele.addClass(CLASS_IS_SELECTED);
|
||||
}
|
||||
|
||||
function collectSelectID() {
|
||||
let $checked = $(".qor-js-table tbody").find(
|
||||
".mdl-checkbox__input:checked"
|
||||
),
|
||||
IDs = [];
|
||||
|
||||
if (!$checked.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$checked.each(function() {
|
||||
IDs.push(
|
||||
$(this)
|
||||
.closest("tr")
|
||||
.data("primary-key")
|
||||
);
|
||||
});
|
||||
|
||||
return IDs;
|
||||
}
|
||||
|
||||
$(document).on("click.qor.openUrl", "[data-url]", function(e) {
|
||||
let $this = $(this),
|
||||
$target = $(e.target),
|
||||
isNewButton = $this.hasClass("qor-button--new"),
|
||||
isEditButton = $this.hasClass("qor-button--edit"),
|
||||
isInTable =
|
||||
($this.is(".qor-table tr[data-url]") ||
|
||||
$this.closest(".qor-js-table").length) &&
|
||||
!$this.closest(".qor-slideout").length, // if table is in slideout, will open bottom sheet
|
||||
openData = $this.data(),
|
||||
actionData,
|
||||
openType = openData.openType,
|
||||
hasSlideoutTheme = $this.parents(".qor-theme-slideout").length,
|
||||
isInSlideout = $this.closest(".qor-slideout").length,
|
||||
isActionButton =
|
||||
$this.hasClass("qor-action-button") ||
|
||||
$this.hasClass("qor-action--button");
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
// if clicking item's menu actions
|
||||
if (
|
||||
$this.data("ajax-form") ||
|
||||
$target.closest(".qor-table--bulking").length ||
|
||||
$target.closest(".qor-button--actions").length ||
|
||||
(!$target.data("url") && $target.is("a")) ||
|
||||
(isInTable && isBottomsheetsOpened())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (openType == "window") {
|
||||
window.location.href = openData.url;
|
||||
return;
|
||||
}
|
||||
|
||||
if (openType == "new_window") {
|
||||
window.open(openData.url, "_blank");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isActionButton) {
|
||||
actionData = collectSelectID();
|
||||
if (actionData) {
|
||||
openData = $.extend({}, openData, {
|
||||
actionData: actionData
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
openData.$target = $target;
|
||||
|
||||
if (!openData.method || openData.method.toUpperCase() == "GET") {
|
||||
// Open in BottmSheet: is action button, open type is bottom-sheet
|
||||
// is action button but opentype == slideout, should open in slideout\
|
||||
// open type is No.1 priority
|
||||
|
||||
if (
|
||||
(openType == "bottomsheet" || isActionButton) &&
|
||||
openType != "slideout"
|
||||
) {
|
||||
// if is bulk action and no item selected
|
||||
if (
|
||||
isActionButton &&
|
||||
!actionData &&
|
||||
$this.closest('[data-toggle="qor.action.bulk"]').length &&
|
||||
!isInSlideout
|
||||
) {
|
||||
window.QOR.qorConfirm(openData.errorNoItem);
|
||||
return false;
|
||||
}
|
||||
|
||||
BottomSheets.open(openData);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Slideout or New Page: table items, new button, edit button
|
||||
if (
|
||||
openType == "slideout" ||
|
||||
isInTable ||
|
||||
(isNewButton && !isBottomsheetsOpened()) ||
|
||||
isEditButton
|
||||
) {
|
||||
if (openType == "slideout" || hasSlideoutTheme) {
|
||||
if ($this.hasClass(CLASS_IS_SELECTED)) {
|
||||
Slideout.hide();
|
||||
toggleSelectedCss();
|
||||
return false;
|
||||
} else {
|
||||
Slideout.open(openData);
|
||||
toggleSelectedCss($this);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
window.location.href = openData.url;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Open in BottmSheet: slideout is opened or openType is Bottom Sheet
|
||||
if (isSlideoutOpened() || (isNewButton && isBottomsheetsOpened())) {
|
||||
BottomSheets.open(openData);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Other clicks
|
||||
if (hasSlideoutTheme) {
|
||||
Slideout.open(openData);
|
||||
return false;
|
||||
} else {
|
||||
BottomSheets.open(openData);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
33
app/views/qor/assets/javascripts/app/search.js
Normal file
33
app/views/qor/assets/javascripts/app/search.js
Normal file
@@ -0,0 +1,33 @@
|
||||
$(function () {
|
||||
|
||||
'use strict';
|
||||
|
||||
var location = window.location;
|
||||
|
||||
$('.qor-search').each(function () {
|
||||
var $this = $(this);
|
||||
var $input = $this.find('.qor-search__input');
|
||||
var $clear = $this.find('.qor-search__clear');
|
||||
var isSearched = !!$input.val();
|
||||
|
||||
var emptySearch = function () {
|
||||
var search = location.search.replace(new RegExp($input.attr('name') + '\\=?\\w*'), '');
|
||||
if (search == '?'){
|
||||
location.href = location.href.split('?')[0];
|
||||
} else {
|
||||
location.search = location.search.replace(new RegExp($input.attr('name') + '\\=?\\w*'), '');
|
||||
}
|
||||
};
|
||||
|
||||
$this.closest('.qor-page__header').addClass('has-search');
|
||||
$('header.mdl-layout__header').addClass('has-search');
|
||||
|
||||
$clear.on('click', function () {
|
||||
if ($input.val() || isSearched) {
|
||||
emptySearch();
|
||||
} else {
|
||||
$this.removeClass('is-dirty');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
5
app/views/qor/assets/javascripts/datetimepicker.js
Normal file
5
app/views/qor/assets/javascripts/datetimepicker.js
Normal file
File diff suppressed because one or more lines are too long
1
app/views/qor/assets/javascripts/qor.js
Normal file
1
app/views/qor/assets/javascripts/qor.js
Normal file
File diff suppressed because one or more lines are too long
1458
app/views/qor/assets/javascripts/qor/datepicker.js
Normal file
1458
app/views/qor/assets/javascripts/qor/datepicker.js
Normal file
File diff suppressed because it is too large
Load Diff
408
app/views/qor/assets/javascripts/qor/qor-action.js
Normal file
408
app/views/qor/assets/javascripts/qor/qor-action.js
Normal file
@@ -0,0 +1,408 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
let Mustache = window.Mustache,
|
||||
QOR = window.QOR,
|
||||
NAMESPACE = 'qor.action',
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_CLICK = 'click.' + NAMESPACE,
|
||||
EVENT_UNDO = 'undo.' + NAMESPACE,
|
||||
CLASS_ACTION_FORMS = '.qor-action-forms',
|
||||
CLASS_MENU_ACTIONS = '[data-ajax-form="true"][data-method]',
|
||||
CLASS_BUTTON_BULKS = '.qor-action-bulk-buttons',
|
||||
CLASS_TABLE = '.qor-page .qor-table-container',
|
||||
CLASS_TABLE_BULK = '.qor-table--bulking',
|
||||
CLASS_TABLE_BULK_TR = '.qor-table--bulking tbody tr',
|
||||
CLASS_IS_UNDO = 'is_undo',
|
||||
CLASS_TABLE_MDL = 'mdl-data-table--selectable',
|
||||
CLASS_SLIDEOUT = '.qor-slideout',
|
||||
ACTION_FORM_DATA = 'primary_values[]',
|
||||
CLASS_HEADER_TOGGLE = '.qor-page__header .qor-actions, .qor-page__header .qor-search-container',
|
||||
CLASS_BODY_LOADING = ".qor-body__loading";
|
||||
|
||||
function QorAction(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorAction.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.ajaxForm = {};
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorAction.prototype = {
|
||||
constructor: QorAction,
|
||||
|
||||
init: function() {
|
||||
this.bind();
|
||||
this.initActions();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$element.on(EVENT_CLICK, '.qor-action--bulk', this.renderBulkTable.bind(this)).on(EVENT_CLICK, '.qor-action--exit-bulk', this.removeBulkTable.bind(this));
|
||||
|
||||
$(document)
|
||||
.on(EVENT_CLICK, CLASS_TABLE_BULK_TR, this.handleBulkTableClick.bind(this))
|
||||
.on(EVENT_CLICK, CLASS_MENU_ACTIONS, this.clickAjaxButton.bind(this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$element.off(EVENT_CLICK);
|
||||
|
||||
$(document)
|
||||
.off(EVENT_CLICK, CLASS_TABLE_BULK_TR, this.handleBulkTableClick)
|
||||
.off(EVENT_CLICK, CLASS_MENU_ACTIONS, this.clickAjaxButton);
|
||||
},
|
||||
|
||||
initActions: function() {
|
||||
if (!$(CLASS_TABLE).find('table').length) {
|
||||
$(CLASS_BUTTON_BULKS).hide();
|
||||
$('.qor-page__header a.qor-action--button').hide();
|
||||
}
|
||||
},
|
||||
|
||||
collectFormData: function() {
|
||||
let checkedInputs = $(CLASS_TABLE_BULK).find('.mdl-checkbox__input:checked'),
|
||||
formData = [],
|
||||
normalFormData = [],
|
||||
tempObj;
|
||||
|
||||
if (checkedInputs.length) {
|
||||
checkedInputs.each(function() {
|
||||
let id = $(this)
|
||||
.closest('tr')
|
||||
.data('primary-key');
|
||||
|
||||
tempObj = {};
|
||||
if (id) {
|
||||
formData.push({
|
||||
name: ACTION_FORM_DATA,
|
||||
value: id.toString()
|
||||
});
|
||||
|
||||
tempObj[ACTION_FORM_DATA] = id.toString();
|
||||
normalFormData.push(tempObj);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.ajaxForm.formData = formData;
|
||||
this.ajaxForm.normalFormData = normalFormData;
|
||||
return this.ajaxForm;
|
||||
},
|
||||
|
||||
actionSubmit: function($action) {
|
||||
this.submit($action);
|
||||
return false;
|
||||
},
|
||||
|
||||
handleBulkTableClick: function(e) {
|
||||
let $target = $(e.target).closest('tr'),
|
||||
$firstTd = $target.find('td').first(),
|
||||
$checkbox = $firstTd.find('.mdl-js-checkbox');
|
||||
|
||||
$checkbox.toggleClass('is-checked');
|
||||
$target.toggleClass('is-selected');
|
||||
$firstTd.find('input').prop('checked', $checkbox.hasClass('is-checked'));
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
adjustPageBodyStyle: function(isRender) {
|
||||
let $pageHeader = $('.qor-page > .qor-page__header'),
|
||||
$pageBody = $('.qor-page > .qor-page__body'),
|
||||
triggerHeight = $pageHeader.find('.qor-page-subnav__header').length ? 96 : 48;
|
||||
|
||||
if (isRender) {
|
||||
if ($pageHeader.height() > triggerHeight) {
|
||||
$pageBody.css('padding-top', $pageHeader.height());
|
||||
}
|
||||
} else {
|
||||
if (parseInt($pageBody.css('padding-top')) > triggerHeight) {
|
||||
$pageBody.css('padding-top', '');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
renderBulkTable: function() {
|
||||
let $body = $('body');
|
||||
|
||||
if ($body.hasClass('qor-slideout-open')) {
|
||||
$body.data('qor.slideout').hide();
|
||||
}
|
||||
|
||||
$('.qor-table__inner-list').remove();
|
||||
this.toggleBulkButtons();
|
||||
this.enableTableMDL();
|
||||
this.adjustPageBodyStyle(true);
|
||||
},
|
||||
|
||||
removeBulkTable: function() {
|
||||
this.toggleBulkButtons();
|
||||
this.disableTableMDL();
|
||||
this.adjustPageBodyStyle();
|
||||
},
|
||||
|
||||
enableTableMDL: function() {
|
||||
$(CLASS_TABLE)
|
||||
.find('table')
|
||||
.removeAttr('data-upgraded')
|
||||
.addClass(CLASS_TABLE_MDL)
|
||||
.trigger('enable');
|
||||
},
|
||||
|
||||
disableTableMDL: function() {
|
||||
$(CLASS_TABLE)
|
||||
.find('table')
|
||||
.removeClass(CLASS_TABLE_MDL)
|
||||
.find('tr')
|
||||
.removeClass('is-selected')
|
||||
.find('td:first,th:first')
|
||||
.remove();
|
||||
},
|
||||
|
||||
toggleBulkButtons: function() {
|
||||
this.$element.find(CLASS_ACTION_FORMS).toggle();
|
||||
$(CLASS_BUTTON_BULKS)
|
||||
.find('button')
|
||||
.toggleClass('hidden');
|
||||
|
||||
$(CLASS_TABLE)
|
||||
.toggleClass('qor-table--bulking')
|
||||
.find('.qor-table__actions')
|
||||
.toggle();
|
||||
|
||||
$(CLASS_HEADER_TOGGLE).toggle();
|
||||
},
|
||||
|
||||
clickAjaxButton: function(e) {
|
||||
let $target = $(e.target);
|
||||
|
||||
this.collectFormData();
|
||||
this.ajaxForm.properties = $target.data();
|
||||
this.submit($target);
|
||||
return false;
|
||||
},
|
||||
|
||||
renderFlashMessage: function(data) {
|
||||
let flashMessageTmpl = QorAction.FLASHMESSAGETMPL;
|
||||
Mustache.parse(flashMessageTmpl);
|
||||
return Mustache.render(flashMessageTmpl, data);
|
||||
},
|
||||
|
||||
addLoading: function() {
|
||||
$(CLASS_BODY_LOADING).remove();
|
||||
var $loading = $(QorAction.TEMPLATE_LOADING);
|
||||
$loading.appendTo($("body")).trigger("enable.qor.material");
|
||||
},
|
||||
|
||||
submit: function($actionButton) {
|
||||
let _this = this,
|
||||
ajaxForm = this.ajaxForm || {},
|
||||
properties = ajaxForm.properties || $actionButton.data();
|
||||
|
||||
|
||||
if($actionButton.hasClass("qor-action-disabled")){
|
||||
return false;
|
||||
}
|
||||
|
||||
if (properties.fromIndex && (!ajaxForm.formData || !ajaxForm.formData.length)) {
|
||||
QOR.qorConfirm(ajaxForm.properties.errorNoItem);
|
||||
return;
|
||||
}
|
||||
|
||||
if (properties.confirm) {
|
||||
QOR.qorConfirm(properties, function(confirm) {
|
||||
if (confirm) {
|
||||
_this.handleAjaxSubmit(ajaxForm, $actionButton);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.handleAjaxSubmit(ajaxForm, $actionButton);
|
||||
}
|
||||
},
|
||||
|
||||
handleAjaxSubmit: function(ajaxForm, $actionButton) {
|
||||
let _this = this,
|
||||
$element = this.$element,
|
||||
$parent = $actionButton.closest(".qor-action-forms"),
|
||||
properties = ajaxForm.properties || $actionButton.data(),
|
||||
url = properties.url,
|
||||
undoUrl = properties.undoUrl,
|
||||
isUndo = $actionButton.hasClass(CLASS_IS_UNDO),
|
||||
isInSlideout = $actionButton.closest(CLASS_SLIDEOUT).length,
|
||||
needDisableButtons = $element.length && !isInSlideout;
|
||||
|
||||
if (isUndo) {
|
||||
url = undoUrl; // notification has undo url
|
||||
}
|
||||
|
||||
this.addLoading();
|
||||
if($parent.length){
|
||||
$parent.find('[data-ajax-form="true"][data-method]').addClass("qor-action-disabled");
|
||||
} else {
|
||||
$actionButton.addClass("qor-action-disabled");
|
||||
}
|
||||
|
||||
|
||||
$.ajax(url, {
|
||||
method: properties.method,
|
||||
data: ajaxForm.formData,
|
||||
dataType: properties.datatype || 'json',
|
||||
beforeSend: function() {
|
||||
if (undoUrl) {
|
||||
$actionButton.prop('disabled', true);
|
||||
} else if (needDisableButtons) {
|
||||
_this.switchButtons($element, 1);
|
||||
}
|
||||
|
||||
},
|
||||
success: function(data) {
|
||||
// has undo action
|
||||
if (undoUrl) {
|
||||
$element.trigger(EVENT_UNDO, [$actionButton, isUndo, data]);
|
||||
isUndo ? $actionButton.removeClass(CLASS_IS_UNDO) : $actionButton.addClass(CLASS_IS_UNDO);
|
||||
$actionButton.prop('disabled', false);
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
},
|
||||
error: function(err) {
|
||||
if (err.status == 200) {
|
||||
return;
|
||||
}
|
||||
if (undoUrl) {
|
||||
$actionButton.prop('disabled', false);
|
||||
} else if (needDisableButtons) {
|
||||
_this.switchButtons($element);
|
||||
}
|
||||
|
||||
QOR.handleAjaxError(err);
|
||||
},
|
||||
complete: function(response) {
|
||||
let contentType = response.getResponseHeader('content-type'),
|
||||
disposition = response.getResponseHeader('Content-Disposition');
|
||||
|
||||
$(CLASS_BODY_LOADING).remove();
|
||||
$actionButton.prop('disabled', false);
|
||||
if($parent.length){
|
||||
$parent.find('[data-ajax-form="true"][data-method]').removeClass("qor-action-disabled");
|
||||
} else {
|
||||
$actionButton.removeClass("qor-action-disabled");
|
||||
}
|
||||
|
||||
// handle file download from form submit
|
||||
if (disposition && disposition.indexOf('attachment') !== -1) {
|
||||
var fileNameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/,
|
||||
matches = fileNameRegex.exec(disposition),
|
||||
fileData = {},
|
||||
fileName = '';
|
||||
|
||||
if (matches != null && matches[1]) {
|
||||
fileName = matches[1].replace(/['"]/g, '');
|
||||
}
|
||||
|
||||
if (properties.method) {
|
||||
fileData = $.extend({}, ajaxForm.normalFormData, {
|
||||
_method: properties.method
|
||||
});
|
||||
}
|
||||
|
||||
QOR.qorAjaxHandleFile(url, contentType, fileName, fileData);
|
||||
|
||||
if (undoUrl) {
|
||||
$actionButton.prop('disabled', false);
|
||||
} else {
|
||||
_this.switchButtons($element);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
switchButtons: function($element, disbale) {
|
||||
let needDisbale = disbale ? true : false;
|
||||
$element.find('.qor-action-button').prop('disabled', needDisbale);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorAction.DEFAULTS = {};
|
||||
|
||||
QorAction.TEMPLATE_LOADING = `<div class="qor-body__loading">
|
||||
<div class="mdl-dialog-bg"></div>
|
||||
<div><div class="mdl-spinner mdl-js-spinner is-active qor-layout__bottomsheet-spinner"></div></div>
|
||||
</div>`;
|
||||
|
||||
$.fn.qorSliderAfterShow.qorInsertActionData = function(url, html) {
|
||||
let $action = $(html).find('[data-toggle="qor-action-slideout"]'),
|
||||
$actionForm = $action.find('form'),
|
||||
$checkedItem = $(CLASS_TABLE_BULK).find('.mdl-checkbox__input:checked');
|
||||
|
||||
if ($action.length && $checkedItem.length) {
|
||||
// insert checked value into sliderout form
|
||||
$checkedItem.each(function() {
|
||||
let id = $(this)
|
||||
.closest('tr')
|
||||
.data('primary-key');
|
||||
|
||||
if (id) {
|
||||
$actionForm.prepend('<input class="js-primary-value" type="hidden" name="primary_values[]" value="' + id + '" />');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
QorAction.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
let $this = $(this),
|
||||
data = $this.data(NAMESPACE),
|
||||
fn;
|
||||
|
||||
if (!data) {
|
||||
$this.data(NAMESPACE, (data = new QorAction(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
|
||||
fn.call(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
let options = {},
|
||||
selector = '[data-toggle="qor.action.bulk"]';
|
||||
|
||||
if (!$(selector).length) {
|
||||
$(document).on(EVENT_CLICK, CLASS_MENU_ACTIONS, function(e) {
|
||||
new QorAction().actionSubmit($(e.target));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorAction.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorAction.plugin.call($(selector, e.target), options);
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorAction;
|
||||
});
|
||||
272
app/views/qor/assets/javascripts/qor/qor-advanced-search.js
Normal file
272
app/views/qor/assets/javascripts/qor/qor-advanced-search.js
Normal file
@@ -0,0 +1,272 @@
|
||||
(function(factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(["jquery"], factory);
|
||||
} else if (typeof exports === "object") {
|
||||
// Node / CommonJS
|
||||
factory(require("jquery"));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
"use strict";
|
||||
|
||||
let location = window.location,
|
||||
QOR = window.QOR,
|
||||
NAMESPACE = "qor.advancedsearch",
|
||||
EVENT_ENABLE = "enable." + NAMESPACE,
|
||||
EVENT_DISABLE = "disable." + NAMESPACE,
|
||||
EVENT_CLICK = "click." + NAMESPACE,
|
||||
EVENT_SHOWN = "shown.qor.modal",
|
||||
EVENT_SUBMIT = "submit." + NAMESPACE;
|
||||
|
||||
function getExtraPairs(names) {
|
||||
let pairs = decodeURIComponent(location.search.substr(1)).split("&"),
|
||||
pairsObj = {},
|
||||
pair,
|
||||
i;
|
||||
|
||||
if (pairs.length == 1 && pairs[0] == "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i in pairs) {
|
||||
if (pairs[i] === "") continue;
|
||||
|
||||
pair = pairs[i].split("=");
|
||||
pairsObj[pair[0]] = pair[1];
|
||||
}
|
||||
|
||||
names.forEach(function(item) {
|
||||
delete pairsObj[item];
|
||||
});
|
||||
|
||||
return pairsObj;
|
||||
}
|
||||
|
||||
function QorAdvancedSearch(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend(
|
||||
{},
|
||||
QorAdvancedSearch.DEFAULTS,
|
||||
$.isPlainObject(options) && options
|
||||
);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorAdvancedSearch.prototype = {
|
||||
constructor: QorAdvancedSearch,
|
||||
|
||||
init: function() {
|
||||
this.$form = this.$element.find("form");
|
||||
this.$modal = $(QorAdvancedSearch.MODAL).appendTo("body");
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$element
|
||||
.on(EVENT_SUBMIT, "form", this.submit.bind(this))
|
||||
.on(
|
||||
EVENT_CLICK,
|
||||
".qor-advanced-filter__save",
|
||||
this.showSaveFilter.bind(this)
|
||||
)
|
||||
.on(
|
||||
EVENT_CLICK,
|
||||
".qor-advanced-filter__toggle",
|
||||
this.toggleFilterContent
|
||||
)
|
||||
.on(EVENT_CLICK, ".qor-advanced-filter__close", this.closeFilter)
|
||||
.on(
|
||||
EVENT_CLICK,
|
||||
".qor-advanced-filter__delete",
|
||||
this.deleteSavedFilter
|
||||
);
|
||||
|
||||
this.$modal.on(EVENT_SHOWN, this.start.bind(this));
|
||||
},
|
||||
|
||||
closeFilter: function() {
|
||||
$(".qor-advanced-filter__dropdown").hide();
|
||||
},
|
||||
|
||||
toggleFilterContent: function(e) {
|
||||
$(e.target)
|
||||
.closest(".qor-advanced-filter__toggle")
|
||||
.parent()
|
||||
.find(">[advanced-search-toggle]")
|
||||
.toggle();
|
||||
},
|
||||
|
||||
showSaveFilter: function() {
|
||||
this.$modal.qorModal("show");
|
||||
},
|
||||
|
||||
deleteSavedFilter: function(e) {
|
||||
let $target = $(e.target).closest(".qor-advanced-filter__delete"),
|
||||
$savedFilter = $target.closest(".qor-advanced-filter__savedfilter"),
|
||||
name = $target.data("filter-name"),
|
||||
url = location.pathname,
|
||||
message = {
|
||||
confirm: "Are you sure you want to delete this saved filter?"
|
||||
};
|
||||
|
||||
QOR.qorConfirm(message, function(confirm) {
|
||||
if (confirm) {
|
||||
$.get(url, $.param({ delete_saved_filter: name }))
|
||||
.done(function() {
|
||||
$target.closest("li").remove();
|
||||
if ($savedFilter.find("li").length === 0) {
|
||||
$savedFilter.remove();
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
QOR.qorConfirm("Server error, please try again!");
|
||||
});
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
|
||||
start: function() {
|
||||
this.$modal
|
||||
.trigger("enable.qor.material")
|
||||
.on(
|
||||
EVENT_CLICK,
|
||||
".qor-advanced-filter__savefilter",
|
||||
this.saveFilter.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
saveFilter: function() {
|
||||
let name = this.$modal.find("#qor-advanced-filter__savename").val();
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$form
|
||||
.prepend(
|
||||
`<input type="hidden" name="filter_saving_name" value="${name}" />`
|
||||
)
|
||||
.submit();
|
||||
},
|
||||
|
||||
submit: function() {
|
||||
let $form = this.$form,
|
||||
formArr = $form.find("input[name],select[name]"),
|
||||
names = [],
|
||||
extraPairs,
|
||||
$bottomsheet = $form.closest(".qor-bottomsheets"),
|
||||
params = $form.serialize();
|
||||
|
||||
formArr.each(function() {
|
||||
names.push($(this).attr("name"));
|
||||
});
|
||||
|
||||
extraPairs = getExtraPairs(names);
|
||||
|
||||
if (!$.isEmptyObject(extraPairs)) {
|
||||
for (let key in extraPairs) {
|
||||
if (extraPairs.hasOwnProperty(key)) {
|
||||
$form.prepend(
|
||||
`<input type="hidden" name=${key} value=${extraPairs[key]} />`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.$element.find(".qor-advanced-filter__dropdown").hide();
|
||||
|
||||
this.removeEmptyPairs($form);
|
||||
|
||||
if ($bottomsheet.length) {
|
||||
if ($bottomsheet.data().url) {
|
||||
let reloadUrl = `${$bottomsheet.data().url}?${params}`;
|
||||
$bottomsheet.trigger("reloadFromUrl.qor.bottomsheets", [reloadUrl]);
|
||||
return false;
|
||||
} else {
|
||||
console.log("dont have base URL! advancedsearch reload failed");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
removeEmptyPairs: function($form) {
|
||||
$form.find("advanced-filter-group").each(function() {
|
||||
let $this = $(this),
|
||||
$input = $this.find("[filter-required]");
|
||||
if ($input.val() == "") {
|
||||
$this.remove();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorAdvancedSearch.DEFAULTS = {};
|
||||
|
||||
QorAdvancedSearch.MODAL = `<div class="qor-modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="mdl-card mdl-shadow--2dp" role="document">
|
||||
<div class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">Save advanced filter</h2>
|
||||
</div>
|
||||
<div class="mdl-card__supporting-text">
|
||||
|
||||
<div class="mdl-textfield mdl-textfield--full-width mdl-js-textfield">
|
||||
<input class="mdl-textfield__input" type="text" id="qor-advanced-filter__savename">
|
||||
<label class="mdl-textfield__label" for="qor-advanced-filter__savename">Please enter name for this filter</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="mdl-card__actions">
|
||||
<a class="mdl-button mdl-button--colored mdl-button--raised qor-advanced-filter__savefilter">Save This Filter</a>
|
||||
<a class="mdl-button mdl-button--colored" data-dismiss="modal">Cancel</a>
|
||||
</div>
|
||||
<div class="mdl-card__menu">
|
||||
<button class="mdl-button mdl-button--icon" data-dismiss="modal" aria-label="close">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
QorAdvancedSearch.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
let $this = $(this),
|
||||
data = $this.data(NAMESPACE),
|
||||
fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorAdvancedSearch(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === "string" && $.isFunction((fn = data[options]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
let selector = '[data-toggle="qor.advancedsearch"]',
|
||||
options;
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorAdvancedSearch.plugin.call($(selector, e.target), "destroy");
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorAdvancedSearch.plugin.call($(selector, e.target), options);
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorAdvancedSearch;
|
||||
});
|
||||
101
app/views/qor/assets/javascripts/qor/qor-autoheight.js
Normal file
101
app/views/qor/assets/javascripts/qor/qor-autoheight.js
Normal file
@@ -0,0 +1,101 @@
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var NAMESPACE = 'qor.autoheight';
|
||||
var EVENT_ENABLE = 'enable.' + NAMESPACE;
|
||||
var EVENT_DISABLE = 'disable.' + NAMESPACE;
|
||||
var EVENT_INPUT = 'input';
|
||||
|
||||
function QorAutoheight(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorAutoheight.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorAutoheight.prototype = {
|
||||
constructor: QorAutoheight,
|
||||
|
||||
init: function () {
|
||||
var $this = this.$element;
|
||||
|
||||
this.paddingTop = parseInt($this.css('padding-top'), 10);
|
||||
this.paddingBottom = parseInt($this.css('padding-bottom'), 10);
|
||||
this.resize();
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function () {
|
||||
this.$element.on(EVENT_INPUT, $.proxy(this.resize, this));
|
||||
},
|
||||
|
||||
unbind: function () {
|
||||
this.$element.off(EVENT_INPUT, this.resize);
|
||||
},
|
||||
|
||||
resize: function () {
|
||||
var $this = this.$element;
|
||||
var scrollHeight = $this.prop('scrollHeight');
|
||||
|
||||
if(scrollHeight){
|
||||
$this.height('auto').height(scrollHeight - this.paddingTop - this.paddingBottom);
|
||||
} else {
|
||||
$this.height('40px');
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.unbind();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorAutoheight.DEFAULTS = {};
|
||||
|
||||
QorAutoheight.plugin = function (options) {
|
||||
return this.each(function () {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorAutoheight(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction(fn = data[options])) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function () {
|
||||
var selector = 'textarea.qor-js-autoheight';
|
||||
|
||||
$(document).
|
||||
on(EVENT_DISABLE, function (e) {
|
||||
QorAutoheight.plugin.call($(selector, e.target), 'destroy');
|
||||
}).
|
||||
on(EVENT_ENABLE, function (e) {
|
||||
QorAutoheight.plugin.call($(selector, e.target));
|
||||
}).
|
||||
triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorAutoheight;
|
||||
|
||||
});
|
||||
754
app/views/qor/assets/javascripts/qor/qor-bottomsheets.js
Normal file
754
app/views/qor/assets/javascripts/qor/qor-bottomsheets.js
Normal file
@@ -0,0 +1,754 @@
|
||||
(function(factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(["jquery"], factory);
|
||||
} else if (typeof exports === "object") {
|
||||
// Node / CommonJS
|
||||
factory(require("jquery"));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
"use strict";
|
||||
|
||||
let _ = window._,
|
||||
FormData = window.FormData,
|
||||
QOR_Translations = window.QOR_Translations,
|
||||
NAMESPACE = "qor.bottomsheets",
|
||||
EVENT_CLICK = "click." + NAMESPACE,
|
||||
EVENT_SUBMIT = "submit." + NAMESPACE,
|
||||
EVENT_SUBMITED = "ajaxSuccessed." + NAMESPACE,
|
||||
EVENT_RELOAD = "reload." + NAMESPACE,
|
||||
EVENT_RELOADFROMURL = "reloadFromUrl." + NAMESPACE,
|
||||
EVENT_BOTTOMSHEET_BEFORESEND = "bottomsheetBeforeSend." + NAMESPACE,
|
||||
EVENT_BOTTOMSHEET_LOADED = "bottomsheetLoaded." + NAMESPACE,
|
||||
EVENT_BOTTOMSHEET_CLOSED = "bottomsheetClosed." + NAMESPACE,
|
||||
EVENT_BOTTOMSHEET_SUBMIT = "bottomsheetSubmitComplete." + NAMESPACE,
|
||||
EVENT_HIDDEN = "hidden." + NAMESPACE,
|
||||
EVENT_KEYUP = "keyup." + NAMESPACE,
|
||||
CLASS_OPEN = "qor-bottomsheets-open",
|
||||
CLASS_IS_SHOWN = "is-shown",
|
||||
CLASS_IS_SLIDED = "is-slided",
|
||||
CLASS_MAIN_CONTENT = ".mdl-layout__content.qor-page",
|
||||
CLASS_BODY_CONTENT = ".qor-page__body",
|
||||
CLASS_BODY_HEAD = ".qor-page__header",
|
||||
CLASS_BOTTOMSHEETS_FILTER = ".qor-bottomsheet__filter",
|
||||
CLASS_BOTTOMSHEETS_BUTTON = ".qor-bottomsheets__search-button",
|
||||
CLASS_BOTTOMSHEETS_INPUT = ".qor-bottomsheets__search-input",
|
||||
URL_GETQOR = "http://www.getqor.com/";
|
||||
|
||||
function getUrlParameter(name, search) {
|
||||
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
|
||||
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
|
||||
var results = regex.exec(decodeURIComponent(search));
|
||||
return results === null
|
||||
? ""
|
||||
: results[1].replace(/\+/g, " ");
|
||||
}
|
||||
|
||||
function updateQueryStringParameter(key, value, uri) {
|
||||
var escapedkey = String(key).replace(/[\\^$*+?.()|[\]{}]/g, "\\$&"),
|
||||
re = new RegExp("([?&])" + escapedkey + "=.*?(&|$)", "i"),
|
||||
separator = uri.indexOf("?") !== -1 ? "&" : "?";
|
||||
|
||||
if (uri.match(re)) {
|
||||
if (value) {
|
||||
return uri.replace(re, "$1" + key + "=" + value + "$2");
|
||||
} else {
|
||||
if (RegExp.$1 === "?" || RegExp.$1 === RegExp.$2) {
|
||||
return uri.replace(re, "$1");
|
||||
} else {
|
||||
return uri.replace(re, "");
|
||||
}
|
||||
}
|
||||
} else if (value) {
|
||||
return uri + separator + key + "=" + value;
|
||||
}
|
||||
}
|
||||
|
||||
function pushArrary($ele, isScript) {
|
||||
let array = [],
|
||||
prop = "href";
|
||||
|
||||
isScript && (prop = "src");
|
||||
$ele.each(function() {
|
||||
array.push($(this).attr(prop));
|
||||
});
|
||||
return _.uniq(array);
|
||||
}
|
||||
|
||||
function execSlideoutEvents(url, response) {
|
||||
// exec qorSliderAfterShow after script loaded
|
||||
var qorSliderAfterShow = $.fn.qorSliderAfterShow;
|
||||
for (var name in qorSliderAfterShow) {
|
||||
if (
|
||||
qorSliderAfterShow.hasOwnProperty(name) &&
|
||||
!qorSliderAfterShow[name]["isLoadedInBottomSheet"] &&
|
||||
name != "initPublishForm" &&
|
||||
name != "qorActivityinit"
|
||||
) {
|
||||
qorSliderAfterShow[name]["isLoadedInBottomSheet"] = true;
|
||||
qorSliderAfterShow[name].call(this, url, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadScripts(srcs, data, callback) {
|
||||
let scriptsLoaded = 0;
|
||||
|
||||
for (let i = 0, len = srcs.length; i < len; i++) {
|
||||
let script = document.createElement("script");
|
||||
|
||||
script.onload = function() {
|
||||
scriptsLoaded++;
|
||||
|
||||
if (scriptsLoaded === srcs.length) {
|
||||
if ($.isFunction(callback)) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
if (data && data.url && data.response) {
|
||||
execSlideoutEvents(data.url, data.response);
|
||||
}
|
||||
};
|
||||
|
||||
script.src = srcs[i];
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
|
||||
function loadStyles(srcs) {
|
||||
let ss = document.createElement("link"),
|
||||
src = srcs.shift();
|
||||
|
||||
ss.type = "text/css";
|
||||
ss.rel = "stylesheet";
|
||||
ss.onload = function() {
|
||||
if (srcs.length) {
|
||||
loadStyles(srcs);
|
||||
}
|
||||
};
|
||||
ss.href = src;
|
||||
document.getElementsByTagName("head")[0].appendChild(ss);
|
||||
}
|
||||
|
||||
function compareScripts($scripts) {
|
||||
let $currentPageScripts = $("script"),
|
||||
slideoutScripts = pushArrary($scripts, true),
|
||||
currentPageScripts = pushArrary($currentPageScripts, true),
|
||||
scriptDiff = _.difference(slideoutScripts, currentPageScripts);
|
||||
return scriptDiff;
|
||||
}
|
||||
|
||||
function compareLinks($links) {
|
||||
let $currentStyles = $("link"),
|
||||
slideoutStyles = pushArrary($links),
|
||||
currentStyles = pushArrary($currentStyles),
|
||||
styleDiff = _.difference(slideoutStyles, currentStyles);
|
||||
|
||||
return styleDiff;
|
||||
}
|
||||
|
||||
function QorBottomSheets(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend(
|
||||
{},
|
||||
QorBottomSheets.DEFAULTS,
|
||||
$.isPlainObject(options) && options
|
||||
);
|
||||
this.resourseData = {};
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorBottomSheets.prototype = {
|
||||
constructor: QorBottomSheets,
|
||||
|
||||
init: function() {
|
||||
this.build();
|
||||
this.bind();
|
||||
},
|
||||
|
||||
build: function() {
|
||||
let $bottomsheets;
|
||||
|
||||
this.$bottomsheets = $bottomsheets = $(QorBottomSheets.TEMPLATE).appendTo(
|
||||
"body"
|
||||
);
|
||||
this.$body = $bottomsheets.find(".qor-bottomsheets__body");
|
||||
this.$title = $bottomsheets.find(".qor-bottomsheets__title");
|
||||
this.$header = $bottomsheets.find(".qor-bottomsheets__header");
|
||||
this.$bodyClass = $("body").prop("class");
|
||||
this.filterURL = "";
|
||||
this.searchParams = "";
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$bottomsheets
|
||||
.on(EVENT_SUBMIT, "form", this.submit.bind(this))
|
||||
.on(EVENT_CLICK, '[data-dismiss="bottomsheets"]', this.hide.bind(this))
|
||||
.on(EVENT_CLICK, ".qor-pagination-container a", this.pagination.bind(this))
|
||||
.on(EVENT_CLICK, CLASS_BOTTOMSHEETS_BUTTON, this.search.bind(this))
|
||||
.on(EVENT_KEYUP, this.keyup.bind(this))
|
||||
.on("selectorChanged.qor.selector", this.selectorChanged.bind(this))
|
||||
.on("filterChanged.qor.filter", this.filterChanged.bind(this))
|
||||
.on(EVENT_RELOADFROMURL, this.reloadFromUrl.bind(this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$bottomsheets
|
||||
.off(EVENT_SUBMIT, "form")
|
||||
.off(EVENT_CLICK)
|
||||
.off("selectorChanged.qor.selector")
|
||||
.off("filterChanged.qor.filter");
|
||||
},
|
||||
|
||||
bindActionData: function(actiondData) {
|
||||
var $form = this.$body
|
||||
.find('[data-toggle="qor-action-slideout"]')
|
||||
.find("form");
|
||||
for (var i = actiondData.length - 1; i >= 0; i--) {
|
||||
$form.prepend(
|
||||
'<input type="hidden" name="primary_values[]" value="' +
|
||||
actiondData[i] +
|
||||
'" />'
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
filterChanged: function(e, search, key) {
|
||||
// if this event triggered:
|
||||
// search: ?locale_mode=locale, ?filters[Color].Value=2
|
||||
// key: search param name: locale_mode
|
||||
|
||||
var loadUrl;
|
||||
|
||||
loadUrl = this.constructloadURL(search, key);
|
||||
loadUrl && this.reload(loadUrl);
|
||||
return false;
|
||||
},
|
||||
|
||||
selectorChanged: function(e, url, key) {
|
||||
// if this event triggered:
|
||||
// url: /admin/!remote_data_searcher/products/Collections?locale=en-US
|
||||
// key: search param key: locale
|
||||
|
||||
var loadUrl;
|
||||
|
||||
loadUrl = this.constructloadURL(url, key);
|
||||
loadUrl && this.reload(loadUrl);
|
||||
return false;
|
||||
},
|
||||
|
||||
keyup: function(e) {
|
||||
var searchInput = this.$bottomsheets.find(CLASS_BOTTOMSHEETS_INPUT);
|
||||
|
||||
if (e.which === 13 && searchInput.length && searchInput.is(":focus")) {
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
|
||||
search: function() {
|
||||
var $bottomsheets = this.$bottomsheets,
|
||||
param = "?keyword=",
|
||||
baseUrl = $bottomsheets.data().url,
|
||||
searchValue = $.trim(
|
||||
$bottomsheets.find(CLASS_BOTTOMSHEETS_INPUT).val()
|
||||
),
|
||||
url = baseUrl + param + searchValue;
|
||||
|
||||
if(/\?/g.test(baseUrl)){
|
||||
url = baseUrl + "&keyword=" + searchValue;
|
||||
}
|
||||
|
||||
this.reload(url);
|
||||
},
|
||||
|
||||
pagination: function(e) {
|
||||
var $ele = $(e.target).closest("a"),
|
||||
url = $ele.prop("href");
|
||||
if (url) {
|
||||
this.reload(url);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
reload: function(url) {
|
||||
var $content = this.$bottomsheets.find(CLASS_BODY_CONTENT);
|
||||
|
||||
this.addLoading($content);
|
||||
this.fetchPage(url);
|
||||
},
|
||||
|
||||
reloadFromUrl: function(e, url) {
|
||||
this.reload(url);
|
||||
},
|
||||
|
||||
fetchPage: function(url) {
|
||||
var $bottomsheets = this.$bottomsheets,
|
||||
_this = this;
|
||||
|
||||
$.get(url, function(response) {
|
||||
var $response = $(response).find(CLASS_MAIN_CONTENT),
|
||||
$responseHeader = $response.find(CLASS_BODY_HEAD),
|
||||
$responseBody = $response.find(CLASS_BODY_CONTENT);
|
||||
|
||||
if ($responseBody.length) {
|
||||
$bottomsheets.find(CLASS_BODY_CONTENT).html($responseBody.html());
|
||||
|
||||
if ($responseHeader.length) {
|
||||
_this.$body
|
||||
.find(CLASS_BODY_HEAD)
|
||||
.html($responseHeader.html())
|
||||
.trigger("enable");
|
||||
_this.addHeaderClass();
|
||||
}
|
||||
// will trigger this event(relaod.qor.bottomsheets) when bottomsheets reload complete: like pagination, filter, action etc.
|
||||
$bottomsheets.trigger(EVENT_RELOAD);
|
||||
} else {
|
||||
_this.reload(url);
|
||||
}
|
||||
}).fail(function() {
|
||||
window.alert("server error, please try again later!");
|
||||
});
|
||||
},
|
||||
|
||||
constructloadURL: function(url, key) {
|
||||
var fakeURL,
|
||||
value,
|
||||
filterURL = this.filterURL,
|
||||
bindUrl = this.$bottomsheets.data().url;
|
||||
|
||||
if (!filterURL) {
|
||||
if (bindUrl) {
|
||||
filterURL = bindUrl;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fakeURL = new URL(URL_GETQOR + url);
|
||||
value = getUrlParameter(key, fakeURL.search);
|
||||
filterURL = this.filterURL = updateQueryStringParameter(
|
||||
key,
|
||||
value,
|
||||
filterURL
|
||||
);
|
||||
|
||||
return filterURL;
|
||||
},
|
||||
|
||||
addHeaderClass: function() {
|
||||
this.$body.find(CLASS_BODY_HEAD).hide();
|
||||
if (
|
||||
this.$bottomsheets
|
||||
.find(CLASS_BODY_HEAD)
|
||||
.children(CLASS_BOTTOMSHEETS_FILTER).length
|
||||
) {
|
||||
this.$body
|
||||
.addClass("has-header")
|
||||
.find(CLASS_BODY_HEAD)
|
||||
.show();
|
||||
}
|
||||
},
|
||||
|
||||
addLoading: function($element) {
|
||||
$element.html("");
|
||||
$(QorBottomSheets.TEMPLATE_LOADING)
|
||||
.appendTo($element)
|
||||
.trigger("enable.qor.material");
|
||||
},
|
||||
|
||||
loadExtraResource: function(data) {
|
||||
let styleDiff = compareLinks(data.$links),
|
||||
scriptDiff = compareScripts(data.$scripts);
|
||||
|
||||
styleDiff.length && loadStyles(styleDiff);
|
||||
scriptDiff.length && loadScripts(scriptDiff, data);
|
||||
},
|
||||
|
||||
loadMedialibraryJS: function($response) {
|
||||
var $script = $response.filter("script"),
|
||||
theme = /theme=media_library/g,
|
||||
src,
|
||||
_this = this;
|
||||
|
||||
$script.each(function() {
|
||||
src = $(this).prop("src");
|
||||
if (theme.test(src)) {
|
||||
var script = document.createElement("script");
|
||||
script.src = src;
|
||||
document.body.appendChild(script);
|
||||
_this.mediaScriptAdded = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
submit: function(e) {
|
||||
let form = e.target,
|
||||
$form = $(form),
|
||||
_this = this,
|
||||
url = $form.prop("action"),
|
||||
formData,
|
||||
$bottomsheets = $form.closest(".qor-bottomsheets"),
|
||||
resourseData = $bottomsheets.data(),
|
||||
ajaxType = resourseData.ajaxType,
|
||||
$submit = $form.find(":submit");
|
||||
|
||||
// will ingore submit event if need handle with other submit event: like select one, many...
|
||||
if (resourseData.ingoreSubmit) {
|
||||
return;
|
||||
}
|
||||
|
||||
// will submit form as normal,
|
||||
// if you need download file after submit form or other things, please add
|
||||
// data-use-normal-submit="true" to form tag
|
||||
// <form action="/admin/products/!action/localize" method="POST" enctype="multipart/form-data" data-normal-submit="true"></form>
|
||||
var normalSubmit = $form.data().normalSubmit;
|
||||
|
||||
if (normalSubmit) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(document).trigger(EVENT_BOTTOMSHEET_BEFORESEND);
|
||||
e.preventDefault();
|
||||
|
||||
formData = new FormData(form);
|
||||
|
||||
$.ajax(url, {
|
||||
method: $form.prop("method"),
|
||||
data: formData,
|
||||
dataType: ajaxType ? ajaxType : "html",
|
||||
processData: false,
|
||||
contentType: false,
|
||||
beforeSend: function() {
|
||||
$submit.prop("disabled", true);
|
||||
},
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
if (resourseData.ajaxMute) {
|
||||
$bottomsheets.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
if (resourseData.ajaxTakeover) {
|
||||
resourseData.$target
|
||||
.parent()
|
||||
.trigger(EVENT_SUBMITED, [data, $bottomsheets]);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle file download from form submit
|
||||
var disposition = jqXHR.getResponseHeader("Content-Disposition");
|
||||
if (disposition && disposition.indexOf("attachment") !== -1) {
|
||||
var fileNameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/,
|
||||
matches = fileNameRegex.exec(disposition),
|
||||
contentType = jqXHR.getResponseHeader("Content-Type"),
|
||||
fileName = "";
|
||||
|
||||
if (matches != null && matches[1]) {
|
||||
fileName = matches[1].replace(/['"]/g, "");
|
||||
}
|
||||
|
||||
window.QOR.qorAjaxHandleFile(url, contentType, fileName, formData);
|
||||
$submit.prop("disabled", false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$(".qor-error").remove();
|
||||
|
||||
var returnUrl = $form.data("returnUrl");
|
||||
var refreshUrl = $form.data("refreshUrl");
|
||||
|
||||
if (refreshUrl) {
|
||||
window.location.href = refreshUrl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (returnUrl == "refresh") {
|
||||
_this.refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
if (returnUrl && returnUrl != "refresh") {
|
||||
_this.load(returnUrl);
|
||||
} else {
|
||||
_this.refresh();
|
||||
}
|
||||
|
||||
$(document).trigger(EVENT_BOTTOMSHEET_SUBMIT);
|
||||
},
|
||||
error: function(err) {
|
||||
window.QOR.handleAjaxError(err);
|
||||
},
|
||||
complete: function() {
|
||||
$submit.prop("disabled", false);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
load: function(url, data, callback) {
|
||||
var options = this.options,
|
||||
method,
|
||||
dataType,
|
||||
load,
|
||||
actionData = data.actionData,
|
||||
resourseData = this.resourseData,
|
||||
selectModal = resourseData.selectModal,
|
||||
ingoreSubmit = resourseData.ingoreSubmit,
|
||||
$bottomsheets = this.$bottomsheets,
|
||||
$header = this.$header,
|
||||
$body = this.$body;
|
||||
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.show();
|
||||
this.addLoading($body);
|
||||
|
||||
this.filterURL = url;
|
||||
$body.removeClass("has-header has-hint");
|
||||
|
||||
data = $.isPlainObject(data) ? data : {};
|
||||
|
||||
method = data.method ? data.method : "GET";
|
||||
dataType = data.datatype ? data.datatype : "html";
|
||||
|
||||
load = $.proxy(function() {
|
||||
$.ajax(url, {
|
||||
method: method,
|
||||
dataType: dataType,
|
||||
success: $.proxy(function(response) {
|
||||
if (method === "GET") {
|
||||
let $response = $(response),
|
||||
$content,
|
||||
bodyClass,
|
||||
loadExtraResourceData = {
|
||||
$scripts: $response.filter("script"),
|
||||
$links: $response.filter("link"),
|
||||
url: url,
|
||||
response: response
|
||||
},
|
||||
hasSearch =
|
||||
selectModal && $response.find(".qor-search-container").length,
|
||||
bodyHtml = response.match(/<\s*body.*>[\s\S]*<\s*\/body\s*>/gi);
|
||||
|
||||
$content = $response.find(CLASS_MAIN_CONTENT);
|
||||
|
||||
if (bodyHtml) {
|
||||
bodyHtml = bodyHtml
|
||||
.join("")
|
||||
.replace(/<\s*body/gi, "<div")
|
||||
.replace(/<\s*\/body/gi, "</div");
|
||||
bodyClass = $(bodyHtml).prop("class");
|
||||
$("body").addClass(bodyClass);
|
||||
}
|
||||
|
||||
if (!$content.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadExtraResource(loadExtraResourceData);
|
||||
|
||||
if (ingoreSubmit) {
|
||||
$content.find(CLASS_BODY_HEAD).remove();
|
||||
}
|
||||
|
||||
$content
|
||||
.find(".qor-button--cancel")
|
||||
.attr("data-dismiss", "bottomsheets");
|
||||
|
||||
$body.html($content.html());
|
||||
this.$title.html($response.find(options.title).html());
|
||||
|
||||
if (data.selectDefaultCreating) {
|
||||
this.$title.append(
|
||||
`<button class="mdl-button mdl-button--primary" type="button" data-load-inline="true" data-select-nohint="${
|
||||
data.selectNohint
|
||||
}" data-select-modal="${
|
||||
data.selectModal
|
||||
}" data-select-listing-url="${data.selectListingUrl}">${
|
||||
data.selectBacktolistTitle
|
||||
}</button>`
|
||||
);
|
||||
}
|
||||
|
||||
if (selectModal) {
|
||||
$body
|
||||
.find(".qor-button--new")
|
||||
.data("ingoreSubmit", true)
|
||||
.data("selectId", resourseData.selectId)
|
||||
.data("loadInline", true);
|
||||
if (
|
||||
selectModal != "one" &&
|
||||
!data.selectNohint &&
|
||||
(typeof resourseData.maxItem === "undefined" ||
|
||||
resourseData.maxItem != "1")
|
||||
) {
|
||||
$body.addClass("has-hint");
|
||||
}
|
||||
if (selectModal == "mediabox" && !this.mediaScriptAdded) {
|
||||
this.loadMedialibraryJS($response);
|
||||
}
|
||||
}
|
||||
|
||||
$header.find(".qor-button--new").remove();
|
||||
this.$title.after($body.find(".qor-button--new"));
|
||||
|
||||
if (hasSearch) {
|
||||
$bottomsheets.addClass("has-search");
|
||||
$header.find(".qor-bottomsheets__search").remove();
|
||||
$header.prepend(QorBottomSheets.TEMPLATE_SEARCH);
|
||||
}
|
||||
|
||||
if (actionData && actionData.length) {
|
||||
this.bindActionData(actionData);
|
||||
}
|
||||
|
||||
if (resourseData.bottomsheetClassname) {
|
||||
$bottomsheets.addClass(resourseData.bottomsheetClassname);
|
||||
}
|
||||
|
||||
$bottomsheets.trigger("enable");
|
||||
|
||||
$bottomsheets.one(EVENT_HIDDEN, function() {
|
||||
$(this).trigger("disable");
|
||||
});
|
||||
|
||||
this.addHeaderClass();
|
||||
$bottomsheets.data(data);
|
||||
|
||||
// handle after opened callback
|
||||
if (callback && $.isFunction(callback)) {
|
||||
callback(this.$bottomsheets);
|
||||
}
|
||||
|
||||
// callback for after bottomSheets loaded HTML
|
||||
$bottomsheets.trigger(EVENT_BOTTOMSHEET_LOADED, [url, response]);
|
||||
} else {
|
||||
if (data.returnUrl) {
|
||||
this.load(data.returnUrl);
|
||||
} else {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
}, this),
|
||||
|
||||
error: $.proxy(function() {
|
||||
this.$bottomsheets.remove();
|
||||
if (!$(".qor-bottomsheets").is(":visible")) {
|
||||
$("body").removeClass(CLASS_OPEN);
|
||||
}
|
||||
var errors;
|
||||
if ($(".qor-error span").length > 0) {
|
||||
errors = $(".qor-error span")
|
||||
.map(function() {
|
||||
return $(this).text();
|
||||
})
|
||||
.get()
|
||||
.join(", ");
|
||||
} else {
|
||||
errors = QOR_Translations.serverError;
|
||||
}
|
||||
window.alert(errors);
|
||||
}, this)
|
||||
});
|
||||
}, this);
|
||||
|
||||
load();
|
||||
},
|
||||
|
||||
open: function(options, callback) {
|
||||
if (!options.loadInline) {
|
||||
this.init();
|
||||
}
|
||||
this.resourseData = options;
|
||||
this.load(options.url, options, callback);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this.$bottomsheets.addClass(CLASS_IS_SHOWN).get(0).offsetHeight;
|
||||
this.$bottomsheets.addClass(CLASS_IS_SLIDED);
|
||||
$("body").addClass(CLASS_OPEN);
|
||||
},
|
||||
|
||||
hide: function(e) {
|
||||
let $bottomsheets = $(e.target).closest(".qor-bottomsheets"),
|
||||
$datePicker = $(".qor-datepicker").not(".hidden");
|
||||
|
||||
if ($datePicker.length) {
|
||||
$datePicker.addClass("hidden");
|
||||
}
|
||||
|
||||
$bottomsheets.qorSelectCore("destroy");
|
||||
|
||||
$bottomsheets.trigger(EVENT_BOTTOMSHEET_CLOSED).remove();
|
||||
if (!$(".qor-bottomsheets").is(":visible")) {
|
||||
$("body").removeClass(CLASS_OPEN);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
this.$bottomsheets.remove();
|
||||
$("body").removeClass(CLASS_OPEN);
|
||||
|
||||
setTimeout(function() {
|
||||
window.location.reload();
|
||||
}, 350);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorBottomSheets.DEFAULTS = {
|
||||
title: ".qor-form-title, .mdl-layout-title",
|
||||
content: false
|
||||
};
|
||||
|
||||
QorBottomSheets.TEMPLATE_ERROR = `<ul class="qor-error"><li><label><i class="material-icons">error</i><span>[[error]]</span></label></li></ul>`;
|
||||
QorBottomSheets.TEMPLATE_LOADING = `<div style="text-align: center; margin-top: 30px;"><div class="mdl-spinner mdl-js-spinner is-active qor-layout__bottomsheet-spinner"></div></div>`;
|
||||
QorBottomSheets.TEMPLATE_SEARCH = `<div class="qor-bottomsheets__search">
|
||||
<input autocomplete="off" type="text" class="mdl-textfield__input qor-bottomsheets__search-input" placeholder="Search" />
|
||||
<button class="mdl-button mdl-js-button mdl-button--icon qor-bottomsheets__search-button" type="button"><i class="material-icons">search</i></button>
|
||||
</div>`;
|
||||
|
||||
QorBottomSheets.TEMPLATE = `<div class="qor-bottomsheets">
|
||||
<div class="qor-bottomsheets__header">
|
||||
<h3 class="qor-bottomsheets__title"></h3>
|
||||
<button type="button" class="mdl-button mdl-button--icon mdl-js-button mdl-js-repple-effect qor-bottomsheets__close" data-dismiss="bottomsheets">
|
||||
<span class="material-icons">close</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="qor-bottomsheets__body"></div>
|
||||
</div>`;
|
||||
|
||||
QorBottomSheets.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorBottomSheets(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === "string" && $.isFunction((fn = data[options]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.qorBottomSheets = QorBottomSheets.plugin;
|
||||
|
||||
return QorBottomSheets;
|
||||
});
|
||||
131
app/views/qor/assets/javascripts/qor/qor-chooser.js
Normal file
131
app/views/qor/assets/javascripts/qor/qor-chooser.js
Normal file
@@ -0,0 +1,131 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
var NAMESPACE = 'qor.chooser';
|
||||
var EVENT_ENABLE = 'enable.' + NAMESPACE;
|
||||
var EVENT_DISABLE = 'disable.' + NAMESPACE;
|
||||
|
||||
function QorChooser(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorChooser.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorChooser.prototype = {
|
||||
constructor: QorChooser,
|
||||
|
||||
init: function() {
|
||||
let $this = this.$element,
|
||||
select2Data = $this.data(),
|
||||
resetSelect2Width,
|
||||
option = {
|
||||
minimumResultsForSearch: 8,
|
||||
dropdownParent: $this.parent()
|
||||
};
|
||||
let getSelect2AjaxDynamicURL = window.getSelect2AjaxDynamicURL;
|
||||
let remoteImage = select2Data.remoteImage;
|
||||
|
||||
if (select2Data.remoteData) {
|
||||
option.ajax = $.fn.select2.ajaxCommonOptions(select2Data);
|
||||
if (getSelect2AjaxDynamicURL && $.isFunction(getSelect2AjaxDynamicURL)) {
|
||||
option.ajax.url = function() {
|
||||
return getSelect2AjaxDynamicURL(select2Data);
|
||||
};
|
||||
} else {
|
||||
option.ajax.url = select2Data.remoteUrl;
|
||||
}
|
||||
|
||||
option.templateResult = function(data) {
|
||||
let tmpl = $this.parents('.qor-field').find('[name="select2-result-template"]');
|
||||
return $.fn.select2.ajaxFormatResult(data, tmpl, remoteImage);
|
||||
};
|
||||
|
||||
option.templateSelection = function(data) {
|
||||
if (data.loading) return data.text;
|
||||
let tmpl = $this.parents('.qor-field').find('[name="select2-selection-template"]');
|
||||
return $.fn.select2.ajaxFormatResult(data, tmpl, remoteImage);
|
||||
};
|
||||
}
|
||||
|
||||
$this
|
||||
.on('select2:select', function(evt) {
|
||||
$(evt.target).attr('chooser-selected', 'true');
|
||||
})
|
||||
.on('select2:unselect', function(evt) {
|
||||
$(evt.target).attr('chooser-selected', '');
|
||||
});
|
||||
|
||||
$this.select2(option);
|
||||
|
||||
// reset select2 container width
|
||||
this.resetSelect2Width();
|
||||
resetSelect2Width = window._.debounce(this.resetSelect2Width.bind(this), 300);
|
||||
$(window).resize(resetSelect2Width);
|
||||
|
||||
if ($this.val()) {
|
||||
$this.attr('chooser-selected', 'true');
|
||||
}
|
||||
},
|
||||
|
||||
resetSelect2Width: function() {
|
||||
var $container,
|
||||
select2 = this.$element.data().select2;
|
||||
if (select2 && select2.$container) {
|
||||
$container = select2.$container;
|
||||
$container.width($container.parent().width());
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.$element.select2('destroy').removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorChooser.DEFAULTS = {};
|
||||
|
||||
QorChooser.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorChooser(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var selector = 'select[data-toggle="qor.chooser"]';
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorChooser.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorChooser.plugin.call($(selector, e.target));
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorChooser;
|
||||
});
|
||||
218
app/views/qor/assets/javascripts/qor/qor-common.js
Normal file
218
app/views/qor/assets/javascripts/qor/qor-common.js
Normal file
@@ -0,0 +1,218 @@
|
||||
$(function() {
|
||||
let _ = window._,
|
||||
QOR = window.QOR,
|
||||
QOR_Translations = window.QOR_Translations,
|
||||
html = `<div id="dialog" style="display: none;">
|
||||
<div class="mdl-dialog-bg"></div>
|
||||
<div class="mdl-dialog">
|
||||
<div class="mdl-dialog__content">
|
||||
<p><i class="material-icons">warning</i></p>
|
||||
<p class="mdl-dialog__message dialog-message">
|
||||
</p>
|
||||
</div>
|
||||
<div class="mdl-dialog__actions">
|
||||
<button type="button" class="mdl-button mdl-button--raised mdl-button--colored dialog-ok dialog-button" data-type="confirm">
|
||||
${QOR_Translations.okButton}
|
||||
</button>
|
||||
<button type="button" class="mdl-button dialog-cancel dialog-button" data-type="">
|
||||
${QOR_Translations.cancelButton}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`,
|
||||
$dialog = $(html).appendTo("body");
|
||||
|
||||
// ************************************ Refactor window.confirm ************************************
|
||||
$(document)
|
||||
.on("keyup.qor.confirm", function(e) {
|
||||
if (!$dialog.is(":visible")) {
|
||||
return;
|
||||
}
|
||||
if (e.which === 27) {
|
||||
setTimeout(function() {
|
||||
$dialog.hide();
|
||||
QOR.qorConfirmCallback = undefined;
|
||||
}, 100);
|
||||
}
|
||||
if (e.which === 13) {
|
||||
setTimeout(function() {
|
||||
$('.dialog-button[data-type="confirm"]').click();
|
||||
}, 100);
|
||||
}
|
||||
})
|
||||
.on("click.qor.confirm", ".dialog-button", function() {
|
||||
let value = $(this).data("type"),
|
||||
callback = QOR.qorConfirmCallback;
|
||||
|
||||
$.isFunction(callback) && callback(value);
|
||||
$dialog.hide();
|
||||
QOR.qorConfirmCallback = undefined;
|
||||
return false;
|
||||
});
|
||||
|
||||
QOR.qorConfirm = function(data, callback) {
|
||||
let okBtn = $dialog.find(".dialog-ok"),
|
||||
cancelBtn = $dialog.find(".dialog-cancel");
|
||||
|
||||
if (_.isString(data)) {
|
||||
$dialog.find(".dialog-message").text(data);
|
||||
okBtn.text(QOR_Translations.okButton);
|
||||
cancelBtn.text(QOR_Translations.cancelButton);
|
||||
} else if (_.isObject(data)) {
|
||||
if (data.confirmOk && data.confirmCancel) {
|
||||
okBtn.text(data.confirmOk);
|
||||
cancelBtn.text(data.confirmCancel);
|
||||
} else {
|
||||
okBtn.text(QOR_Translations.okButton);
|
||||
cancelBtn.text(QOR_Translations.cancelButton);
|
||||
}
|
||||
|
||||
if(data.icon){
|
||||
$dialog.find('i.material-icons').addClass(data.icon).html(data.icon);
|
||||
}
|
||||
|
||||
$dialog.find(".dialog-message").text(data.confirm);
|
||||
}
|
||||
|
||||
$dialog.show();
|
||||
QOR.qorConfirmCallback = callback;
|
||||
return false;
|
||||
};
|
||||
|
||||
// *******************************************************************************
|
||||
|
||||
// ****************Handle download file from AJAX POST****************************
|
||||
let objectToFormData = function(obj, form) {
|
||||
let formdata = form || new FormData(),
|
||||
key;
|
||||
|
||||
for (var variable in obj) {
|
||||
if (obj.hasOwnProperty(variable) && obj[variable]) {
|
||||
key = variable;
|
||||
}
|
||||
|
||||
if (obj[variable] instanceof Date) {
|
||||
formdata.append(key, obj[variable].toISOString());
|
||||
} else if (
|
||||
typeof obj[variable] === "object" &&
|
||||
!(obj[variable] instanceof File)
|
||||
) {
|
||||
objectToFormData(obj[variable], formdata);
|
||||
} else {
|
||||
formdata.append(key, obj[variable]);
|
||||
}
|
||||
}
|
||||
|
||||
return formdata;
|
||||
};
|
||||
|
||||
QOR.qorAjaxHandleFile = function(url, contentType, fileName, data) {
|
||||
let request = new XMLHttpRequest();
|
||||
|
||||
request.responseType = "arraybuffer";
|
||||
request.open("POST", url, true);
|
||||
request.onload = function() {
|
||||
if (this.status === 200) {
|
||||
let blob = new Blob([this.response], {
|
||||
type: contentType
|
||||
}),
|
||||
url = window.URL.createObjectURL(blob),
|
||||
a = document.createElement("a");
|
||||
|
||||
document.body.appendChild(a);
|
||||
a.href = url;
|
||||
a.download = fileName || "download-" + $.now();
|
||||
a.click();
|
||||
} else {
|
||||
window.alert(QOR_Translations.serverError);
|
||||
}
|
||||
};
|
||||
|
||||
if (_.isObject(data)) {
|
||||
if (Object.prototype.toString.call(data) != "[object FormData]") {
|
||||
data = objectToFormData(data);
|
||||
}
|
||||
|
||||
request.send(data);
|
||||
}
|
||||
};
|
||||
|
||||
// ********************************convert video link********************
|
||||
// linkyoutube: /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.\-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig,
|
||||
// linkvimeo: /https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/,
|
||||
|
||||
let converVideoLinks = function() {
|
||||
let $ele = $(".qor-linkify-object"),
|
||||
linkyoutube = /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.\-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/gi;
|
||||
|
||||
if (!$ele.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ele.each(function() {
|
||||
let url = $(this).data("video-link");
|
||||
if (url.match(linkyoutube)) {
|
||||
$(this).html(
|
||||
`<iframe width="100%" height="100%" src="//www.youtube.com/embed/${url.replace(
|
||||
linkyoutube,
|
||||
"$1"
|
||||
)}" frameborder="0" allowfullscreen></iframe>`
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.qorSliderAfterShow.converVideoLinks = converVideoLinks;
|
||||
converVideoLinks();
|
||||
|
||||
// ********************************Qor Handle AJAX error********************
|
||||
QOR.handleAjaxError = function(err) {
|
||||
let $body = $("body"),
|
||||
rJSON = err.responseJSON,
|
||||
rText = err.responseText,
|
||||
$error = $(`<ul class="qor-alert qor-error" data-dismissible="true"><button type="button" class="mdl-button mdl-button--icon" data-dismiss="alert">
|
||||
<i class="material-icons">close</i>
|
||||
</button></ul>`);
|
||||
|
||||
$body.find(".qor-alert").remove();
|
||||
|
||||
if (err.status === 422) {
|
||||
if (rJSON) {
|
||||
let errors = rJSON.errors,
|
||||
$errorContent = "";
|
||||
|
||||
if ($.isArray(errors)) {
|
||||
for (let i = 0; i < errors.length; i++) {
|
||||
$errorContent += `<li>
|
||||
<i class="material-icons">error</i>
|
||||
<span>${errors[i]}</span>
|
||||
</li>`;
|
||||
}
|
||||
} else {
|
||||
$errorContent = `<li>
|
||||
<i class="material-icons">error</i>
|
||||
<span>${errors}</span>
|
||||
</li>`;
|
||||
}
|
||||
$error.append($errorContent);
|
||||
} else {
|
||||
$error = $(rText).find(".qor-error");
|
||||
}
|
||||
} else {
|
||||
$error.append(`<li>
|
||||
<i class="material-icons">error</i>
|
||||
<span>${err.statusText}</span>
|
||||
</li>`);
|
||||
}
|
||||
|
||||
$error.prependTo($body);
|
||||
setTimeout(function() {
|
||||
$error.addClass("qor-alert__active");
|
||||
}, 50);
|
||||
|
||||
setTimeout(function() {
|
||||
$('.qor-alert[data-dismissible="true"]').removeClass("qor-alert__active");
|
||||
$("#qor-submit-loading").remove();
|
||||
}, 6000);
|
||||
};
|
||||
});
|
||||
126
app/views/qor/assets/javascripts/qor/qor-config.js
Normal file
126
app/views/qor/assets/javascripts/qor/qor-config.js
Normal file
@@ -0,0 +1,126 @@
|
||||
// init for slideout after show event
|
||||
$.fn.qorSliderAfterShow = $.fn.qorSliderAfterShow || {};
|
||||
window.QOR = {
|
||||
$formLoading: '<div id="qor-submit-loading" class="clearfix"><div class="mdl-spinner mdl-spinner--single-color mdl-js-spinner is-active"></div></div>'
|
||||
};
|
||||
|
||||
String.prototype.escapeSymbol = function() {
|
||||
var tagsToReplace = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"' : '"',
|
||||
"'" : ''',
|
||||
'/' : '/'
|
||||
};
|
||||
return this.replace(/[&<>"']/g, function(tag) {
|
||||
return tagsToReplace[tag] || tag;
|
||||
});
|
||||
};
|
||||
|
||||
// change Mustache tags from {{}} to [[]]
|
||||
window.Mustache && (window.Mustache.tags = ['[[', ']]']);
|
||||
|
||||
// clear close alert after ajax complete
|
||||
$(document).ajaxComplete(function(event, xhr, settings) {
|
||||
if (settings.type == 'POST' || settings.type == 'PUT') {
|
||||
if ($.fn.qorSlideoutBeforeHide) {
|
||||
$.fn.qorSlideoutBeforeHide = null;
|
||||
window.onbeforeunload = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// select2 ajax common options
|
||||
// $.fn.select2 = $.fn.select2 || function(){};
|
||||
$.fn.select2.ajaxCommonOptions = function(select2Data) {
|
||||
let remoteDataPrimaryKey = select2Data.remoteDataPrimaryKey;
|
||||
|
||||
return {
|
||||
dataType: 'json',
|
||||
headers: getSelect2Header(select2Data),
|
||||
cache: true,
|
||||
delay: 250,
|
||||
data: function(params) {
|
||||
return {
|
||||
keyword: params.term, // search term
|
||||
page: params.page,
|
||||
per_page: 20
|
||||
};
|
||||
},
|
||||
processResults: function(data, params) {
|
||||
// parse the results into the format expected by Select2
|
||||
// since we are using custom formatting functions we do not need to
|
||||
// alter the remote JSON data, except to indicate that infinite
|
||||
// scrolling can be used
|
||||
params.page = params.page || 1;
|
||||
|
||||
var processedData = $.map(data, function(obj) {
|
||||
obj.id = obj[remoteDataPrimaryKey] || obj.primaryKey || obj.Id || obj.ID;
|
||||
return obj;
|
||||
});
|
||||
|
||||
return {
|
||||
results: processedData,
|
||||
pagination: {
|
||||
more: processedData.length >= 20
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// select2 ajax common options
|
||||
// format ajax template data
|
||||
$.fn.select2.ajaxFormatResult = function(data, tmpl, remoteDataImage) {
|
||||
var result = '';
|
||||
|
||||
if (data.loading) {
|
||||
return data.text;
|
||||
}
|
||||
|
||||
console.log('select2.ajaxFormatResult: Data');
|
||||
console.log(data);
|
||||
|
||||
console.log('select2.ajaxFormatResult: has remote image');
|
||||
console.log(remoteDataImage);
|
||||
|
||||
if (remoteDataImage) {
|
||||
var resultName = data.text || data.Name || data.Title || data.Code || data[Object.keys(data)[0]];
|
||||
var imageUrl = data.Image;
|
||||
if (imageUrl) {
|
||||
result = '<div class="select2-results__option-withimage">' + '<img src="' + imageUrl + '">' + '<span>' + resultName + '</span></div>';
|
||||
} else {
|
||||
result = '<div class="select2-results__option-withimage">' + resultName + '</span></div>';
|
||||
}
|
||||
|
||||
return $(result);
|
||||
} else {
|
||||
if (tmpl.length > 0) {
|
||||
result = window.Mustache.render(tmpl.html().replace(/{{(.*?)}}/g, '[[$1]]'), data);
|
||||
} else {
|
||||
result = data.text || data.Name || data.Title || data.Code || data[Object.keys(data)[0]];
|
||||
}
|
||||
|
||||
// if is HTML
|
||||
if (/<(.*)(\/>|<\/.+>)/.test(result)) {
|
||||
return $(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
function getSelect2Header() {
|
||||
let data = $('body').data();
|
||||
let selectAjaxHeader = data.selectAjaxHeader;
|
||||
let getSelect2HeaderFunction = window.getSelect2HeaderFunction;
|
||||
let headers = {};
|
||||
|
||||
if (selectAjaxHeader && getSelect2HeaderFunction && $.isFunction(getSelect2HeaderFunction)) {
|
||||
headers[selectAjaxHeader] = getSelect2HeaderFunction();
|
||||
return headers;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
766
app/views/qor/assets/javascripts/qor/qor-cropper.js
Normal file
766
app/views/qor/assets/javascripts/qor/qor-cropper.js
Normal file
@@ -0,0 +1,766 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
let URL = window.URL || window.webkitURL,
|
||||
NAMESPACE = 'qor.cropper',
|
||||
// Events
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_CHANGE = 'change.' + NAMESPACE,
|
||||
EVENT_CLICK = 'click.' + NAMESPACE,
|
||||
EVENT_SHOWN = 'shown.qor.modal',
|
||||
EVENT_HIDDEN = 'hidden.qor.modal',
|
||||
// Classes
|
||||
CLASS_TOGGLE = '.qor-cropper__toggle',
|
||||
CLASS_CANVAS = '.qor-cropper__canvas',
|
||||
CLASS_WRAPPER = '.qor-cropper__wrapper',
|
||||
CLASS_OPTIONS = '.qor-cropper__options',
|
||||
CLASS_SAVE = '.qor-cropper__save',
|
||||
CLASS_DELETE = '.qor-cropper__toggle--delete',
|
||||
CLASS_CROP = '.qor-cropper__toggle--crop',
|
||||
CLASS_UNDO = '.qor-fieldset__undo',
|
||||
HIDDEN_DATA_INPUT = 'input[name="QorResource.MediaOption"]:hidden';
|
||||
|
||||
function capitalize(str) {
|
||||
if (typeof str === 'string') {
|
||||
str = str.charAt(0).toUpperCase() + str.substr(1);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function getLowerCaseKeyObject(obj) {
|
||||
let newObj = {},
|
||||
key;
|
||||
|
||||
if ($.isPlainObject(obj)) {
|
||||
for (key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
newObj[String(key).toLowerCase()] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
function getValueByNoCaseKey(obj, key) {
|
||||
let originalKey = String(key),
|
||||
lowerCaseKey = originalKey.toLowerCase(),
|
||||
upperCaseKey = originalKey.toUpperCase(),
|
||||
capitalizeKey = capitalize(originalKey);
|
||||
|
||||
if ($.isPlainObject(obj)) {
|
||||
return obj[lowerCaseKey] || obj[capitalizeKey] || obj[upperCaseKey];
|
||||
}
|
||||
}
|
||||
|
||||
function replaceText(str, data) {
|
||||
if (typeof str === 'string') {
|
||||
if (typeof data === 'object') {
|
||||
$.each(data, function(key, val) {
|
||||
str = str.replace('$[' + String(key).toLowerCase() + ']', val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function isSVG(url) {
|
||||
return /.svg$/.test(url);
|
||||
}
|
||||
|
||||
function QorCropper(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend(true, {}, QorCropper.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.data = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorCropper.prototype = {
|
||||
constructor: QorCropper,
|
||||
|
||||
init: function() {
|
||||
let options = this.options,
|
||||
$this = this.$element,
|
||||
$parent = $this.closest(options.parent),
|
||||
data,
|
||||
outputValue,
|
||||
fetchUrl,
|
||||
_this = this,
|
||||
imageData;
|
||||
|
||||
if (!$parent.length) {
|
||||
$parent = $this.parent();
|
||||
}
|
||||
|
||||
this.$parent = $parent;
|
||||
this.$output = $parent.find(options.output);
|
||||
this.$formCropInput = $parent.closest('form').find(HIDDEN_DATA_INPUT);
|
||||
this.$list = $parent.find(options.list);
|
||||
|
||||
fetchUrl = this.$output.data('fetchSizedata');
|
||||
|
||||
if (fetchUrl) {
|
||||
$.getJSON(fetchUrl, function(data) {
|
||||
imageData = JSON.parse(data.MediaOption);
|
||||
_this.$output.val(JSON.stringify(data));
|
||||
_this.$formCropInput.val(JSON.stringify(data));
|
||||
_this.data = imageData || {};
|
||||
if (isSVG(imageData.URL || imageData.Url)) {
|
||||
_this.resetImage();
|
||||
}
|
||||
_this.build();
|
||||
_this.bind();
|
||||
});
|
||||
} else {
|
||||
outputValue = $.trim(this.$output.val());
|
||||
if (outputValue) {
|
||||
data = JSON.parse(outputValue);
|
||||
if (isSVG(data.URL || data.Url)) {
|
||||
this.resetImage();
|
||||
}
|
||||
}
|
||||
|
||||
this.data = data || {};
|
||||
|
||||
this.build();
|
||||
this.bind();
|
||||
}
|
||||
},
|
||||
|
||||
resetImage: function() {
|
||||
this.$parent.addClass('is-svg');
|
||||
},
|
||||
|
||||
build: function() {
|
||||
let textData = this.$output.data(),
|
||||
text = {},
|
||||
replaceTexts;
|
||||
|
||||
if (textData) {
|
||||
text = {
|
||||
title: textData.cropperTitle,
|
||||
ok: textData.cropperOk,
|
||||
cancel: textData.cropperCancel
|
||||
};
|
||||
replaceTexts = this.options.text;
|
||||
}
|
||||
|
||||
if (text.ok && text.title && text.cancel) {
|
||||
replaceTexts = text;
|
||||
}
|
||||
|
||||
this.wrap();
|
||||
this.$modal = $(replaceText(QorCropper.MODAL, replaceTexts)).appendTo('body');
|
||||
},
|
||||
|
||||
unbuild: function() {
|
||||
this.$modal.remove();
|
||||
this.unwrap();
|
||||
},
|
||||
|
||||
wrap: function() {
|
||||
let $list = this.$list,
|
||||
$img;
|
||||
|
||||
$img = $list.find('img').not('.is-svg');
|
||||
|
||||
if ($img.length) {
|
||||
$list.find('li').append(QorCropper.TOGGLE);
|
||||
$img.wrap(QorCropper.CANVAS);
|
||||
this.center($img);
|
||||
} else {
|
||||
$list.find(CLASS_CROP).remove();
|
||||
}
|
||||
},
|
||||
|
||||
unwrap: function() {
|
||||
let $list = this.$list;
|
||||
|
||||
$list.find(CLASS_TOGGLE).remove();
|
||||
$list.find(CLASS_CANVAS).each(function() {
|
||||
let $this = $(this);
|
||||
|
||||
$this.before($this.html()).remove();
|
||||
});
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$element.on(EVENT_CHANGE, $.proxy(this.read, this));
|
||||
this.$list.on(EVENT_CLICK, $.proxy(this.click, this));
|
||||
this.$modal.on(EVENT_SHOWN, $.proxy(this.start, this)).on(EVENT_HIDDEN, $.proxy(this.stop, this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$element.off(EVENT_CHANGE, this.read);
|
||||
this.$list.off(EVENT_CLICK, this.click);
|
||||
this.$modal.off(EVENT_SHOWN, this.start).off(EVENT_HIDDEN, this.stop);
|
||||
},
|
||||
|
||||
click: function(e) {
|
||||
let target = e.target,
|
||||
$target,
|
||||
data = this.data,
|
||||
$alert;
|
||||
|
||||
if (target === this.$list[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
$target = $(target);
|
||||
|
||||
if ($target.closest(CLASS_DELETE).length) {
|
||||
data.Delete = true;
|
||||
|
||||
this.$output.val(JSON.stringify(data));
|
||||
this.$formCropInput.val(JSON.stringify(data));
|
||||
|
||||
this.$list.hide();
|
||||
|
||||
$alert = $(QorCropper.ALERT);
|
||||
$alert.find(CLASS_UNDO).one(
|
||||
EVENT_CLICK,
|
||||
function() {
|
||||
$alert.remove();
|
||||
this.$list.show();
|
||||
delete data.Delete;
|
||||
this.$output.val(JSON.stringify(data));
|
||||
this.$formCropInput.val(JSON.stringify(data));
|
||||
}.bind(this)
|
||||
);
|
||||
this.$parent.find('.qor-fieldset').append($alert);
|
||||
}
|
||||
|
||||
if ($target.closest(CLASS_CROP).length) {
|
||||
$target = $target.closest('li').find('img');
|
||||
this.$target = $target;
|
||||
this.$modal.qorModal('show');
|
||||
}
|
||||
},
|
||||
|
||||
read: function(e) {
|
||||
let files = e.target.files,
|
||||
file,
|
||||
$list = this.$list,
|
||||
$alert = this.$parent.find('.qor-fieldset__alert');
|
||||
|
||||
$list.show();
|
||||
|
||||
if ($alert.length) {
|
||||
$alert.remove();
|
||||
}
|
||||
|
||||
if (files && files.length) {
|
||||
file = files[0];
|
||||
|
||||
if (/^image\//.test(file.type) && URL) {
|
||||
this.fileType = file.type;
|
||||
this.load(URL.createObjectURL(file));
|
||||
this.$parent.find('.qor-medialibrary__image-desc').show();
|
||||
} else {
|
||||
$list.empty().html(QorCropper.FILE_LIST.replace('{{filename}}', file.name));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
load: function(url, fromExternal, callback) {
|
||||
let options = this.options,
|
||||
_this = this,
|
||||
$list = this.$list,
|
||||
$ul = $(QorCropper.LIST),
|
||||
data = this.data || {},
|
||||
outputOriginalData = this.$output.val() ? JSON.parse(this.$output.val()) : {},
|
||||
outputOriginalDataType = ['Video', 'SelectedType', 'Description'],
|
||||
fileType = this.fileType,
|
||||
$image,
|
||||
imageLength;
|
||||
|
||||
// media box will use load method, has it's own html structure.
|
||||
if (!fromExternal) {
|
||||
$list.find('ul').remove();
|
||||
$list.html($ul);
|
||||
}
|
||||
|
||||
$image = $list.find('img');
|
||||
this.wrap();
|
||||
|
||||
imageLength = $image.length;
|
||||
$image
|
||||
.one('load', function() {
|
||||
if (fileType === 'image/svg+xml') {
|
||||
$list.find(CLASS_TOGGLE).remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
let $this = $(this),
|
||||
naturalWidth = this.naturalWidth,
|
||||
naturalHeight = this.naturalHeight,
|
||||
sizeData = $this.data(),
|
||||
sizeResolution = sizeData.sizeResolution,
|
||||
sizeName = sizeData.sizeName,
|
||||
emulateImageData = {},
|
||||
emulateCropData = {},
|
||||
aspectRatio,
|
||||
width = sizeData.sizeResolutionWidth,
|
||||
height = sizeData.sizeResolutionHeight;
|
||||
|
||||
if (sizeResolution) {
|
||||
if (!width && !height) {
|
||||
width = getValueByNoCaseKey(sizeResolution, 'width');
|
||||
height = getValueByNoCaseKey(sizeResolution, 'height');
|
||||
}
|
||||
if (height && width) {
|
||||
aspectRatio = width / height;
|
||||
} else {
|
||||
aspectRatio = naturalWidth / naturalHeight;
|
||||
}
|
||||
|
||||
if (naturalHeight * aspectRatio > naturalWidth) {
|
||||
width = naturalWidth;
|
||||
height = width / aspectRatio;
|
||||
} else {
|
||||
height = naturalHeight;
|
||||
width = height * aspectRatio;
|
||||
}
|
||||
|
||||
emulateImageData = {
|
||||
naturalWidth: naturalWidth,
|
||||
naturalHeight: naturalHeight
|
||||
};
|
||||
|
||||
emulateCropData = {
|
||||
x: Math.round((naturalWidth - width) / 2),
|
||||
y: Math.round((naturalHeight - height) / 2),
|
||||
width: Math.round(width),
|
||||
height: Math.round(height)
|
||||
};
|
||||
|
||||
_this.preview($this, emulateImageData, emulateCropData);
|
||||
|
||||
if (sizeName) {
|
||||
data.Crop = true;
|
||||
|
||||
if (!data[options.key]) {
|
||||
data[options.key] = {};
|
||||
}
|
||||
|
||||
if (sizeName != 'original') {
|
||||
data[options.key][sizeName] = emulateCropData;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_this.center($this);
|
||||
}
|
||||
|
||||
// Crop, CropOptions and Delete should be BOOL type, if empty should delete,
|
||||
if (data.Crop === '' || !fromExternal) {
|
||||
delete data.Crop;
|
||||
}
|
||||
|
||||
if (!fromExternal) {
|
||||
data.CropOptions = null;
|
||||
delete data.Sizes;
|
||||
}
|
||||
|
||||
delete data.Delete;
|
||||
|
||||
// Fix media upload image other property missing bug: ["Video", "SelectedType", "Description"]
|
||||
outputOriginalDataType.forEach((type) => {
|
||||
if (outputOriginalData[type]) {
|
||||
data[type] = outputOriginalData[type];
|
||||
}
|
||||
});
|
||||
|
||||
_this.$output.val(JSON.stringify(data));
|
||||
_this.$formCropInput.val(JSON.stringify(data));
|
||||
|
||||
// callback after load complete
|
||||
if (sizeName && data[options.key] && Object.keys(data[options.key]).length >= imageLength) {
|
||||
if (callback && $.isFunction(callback)) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
})
|
||||
.attr('src', url)
|
||||
.data('originalUrl', url);
|
||||
|
||||
$list.show();
|
||||
},
|
||||
|
||||
start: function() {
|
||||
let options = this.options,
|
||||
$modal = this.$modal,
|
||||
$target = this.$target,
|
||||
sizeData = $target.data(),
|
||||
sizeName = sizeData.sizeName || 'original',
|
||||
sizeResolution = sizeData.sizeResolution,
|
||||
originalUrl = (sizeData && sizeData.originalUrl && $target.attr('data-original-url'))
|
||||
? (/\.original\./.test(sizeData.originalUrl) ? sizeData.originalUrl
|
||||
: /\.original\./.test($target.attr('data-original-url'))
|
||||
? $target.attr('data-original-url')
|
||||
: $target.attr('data-original-url').replace(/file\./, 'file.original.'))
|
||||
: $target.attr('src'),
|
||||
$clone = $(`<img src=${originalUrl}>`),
|
||||
data = this.data || {},
|
||||
_this = this,
|
||||
sizeAspectRatio = NaN,
|
||||
sizeWidth = sizeData.sizeResolutionWidth,
|
||||
sizeHeight = sizeData.sizeResolutionHeight,
|
||||
list;
|
||||
|
||||
if (sizeResolution) {
|
||||
if (!sizeWidth && !sizeHeight) {
|
||||
sizeWidth = getValueByNoCaseKey(sizeResolution, 'width');
|
||||
sizeHeight = getValueByNoCaseKey(sizeResolution, 'height');
|
||||
}
|
||||
sizeAspectRatio = sizeWidth / sizeHeight;
|
||||
}
|
||||
|
||||
if (!data[options.key]) {
|
||||
data[options.key] = {};
|
||||
}
|
||||
|
||||
$modal
|
||||
.trigger('enable.qor.material')
|
||||
.find(CLASS_WRAPPER)
|
||||
.html($clone);
|
||||
|
||||
list = this.getList(sizeAspectRatio);
|
||||
|
||||
if (list) {
|
||||
$modal
|
||||
.find(CLASS_OPTIONS)
|
||||
.show()
|
||||
.append(list);
|
||||
}
|
||||
|
||||
$clone.cropper({
|
||||
aspectRatio: sizeAspectRatio,
|
||||
data: getLowerCaseKeyObject(data[options.key][sizeName]),
|
||||
background: false,
|
||||
movable: false,
|
||||
zoomable: false,
|
||||
scalable: false,
|
||||
rotatable: false,
|
||||
autoCropArea: 1,
|
||||
|
||||
ready: function() {
|
||||
$modal
|
||||
.find('.qor-cropper__options-toggle')
|
||||
.on(EVENT_CLICK, function() {
|
||||
$modal.find('.qor-cropper__options-input').prop('checked', $(this).prop('checked'));
|
||||
})
|
||||
.prop('checked', true);
|
||||
|
||||
$modal.find(CLASS_SAVE).one(EVENT_CLICK, function() {
|
||||
let cropData = $clone.cropper('getData', true),
|
||||
croppedCanvas = $clone.cropper('getCroppedCanvas'),
|
||||
syncData = [],
|
||||
url;
|
||||
|
||||
data.Crop = true;
|
||||
data[options.key][sizeName] = cropData;
|
||||
_this.imageData = $clone.cropper('getImageData');
|
||||
_this.cropData = cropData;
|
||||
|
||||
if (croppedCanvas) {
|
||||
try {
|
||||
url = croppedCanvas.toDataURL();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.log('Please check image Cross-origin setting');
|
||||
}
|
||||
}
|
||||
|
||||
$modal.find(CLASS_OPTIONS + ' input').each(function() {
|
||||
let $this = $(this);
|
||||
|
||||
if ($this.prop('checked')) {
|
||||
syncData.push($this.attr('name'));
|
||||
}
|
||||
});
|
||||
|
||||
_this.output(url, syncData);
|
||||
$modal.qorModal('hide');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
stop: function() {
|
||||
this.$modal
|
||||
.trigger('disable.qor.material')
|
||||
.find(CLASS_WRAPPER + ' > img')
|
||||
.cropper('destroy')
|
||||
.remove()
|
||||
.end()
|
||||
.find(CLASS_OPTIONS)
|
||||
.hide()
|
||||
.find('ul')
|
||||
.remove();
|
||||
},
|
||||
|
||||
getList: function(aspectRatio) {
|
||||
let list = [];
|
||||
|
||||
this.$list
|
||||
.find('img')
|
||||
.not(this.$target)
|
||||
.each(function() {
|
||||
let data = $(this).data(),
|
||||
resolution = data.sizeResolution,
|
||||
name = data.sizeName,
|
||||
width = data.sizeResolutionWidth,
|
||||
height = data.sizeResolutionHeight;
|
||||
|
||||
if (resolution) {
|
||||
if (!width && !height) {
|
||||
width = getValueByNoCaseKey(resolution, 'width');
|
||||
height = getValueByNoCaseKey(resolution, 'height');
|
||||
}
|
||||
|
||||
if (width / height === aspectRatio) {
|
||||
list.push(
|
||||
'<label>' +
|
||||
'<input class="qor-cropper__options-input" type="checkbox" name="' +
|
||||
name +
|
||||
'" checked> ' +
|
||||
'<span>' +
|
||||
name +
|
||||
'<small>(' +
|
||||
width +
|
||||
'×' +
|
||||
height +
|
||||
' px)</small>' +
|
||||
'</span>' +
|
||||
'</label>'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return list.length ? '<ul><li>' + list.join('</li><li>') + '</li></ul>' : '';
|
||||
},
|
||||
|
||||
output: function(url, data) {
|
||||
let $target = this.$target;
|
||||
|
||||
if (url) {
|
||||
this.center($target.attr('src', url), true);
|
||||
} else {
|
||||
this.preview($target);
|
||||
}
|
||||
|
||||
if ($.isArray(data) && data.length) {
|
||||
this.autoCrop(url, data);
|
||||
}
|
||||
|
||||
this.$output.val(JSON.stringify(this.data)).trigger(EVENT_CHANGE);
|
||||
this.$formCropInput.val(JSON.stringify(this.data));
|
||||
},
|
||||
|
||||
preview: function($target, emulateImageData, emulateCropData) {
|
||||
let $canvas = $target.parent(),
|
||||
$container = $canvas.parent(),
|
||||
containerWidth = $container.width(),
|
||||
containerHeight = $container.height(),
|
||||
imageData = emulateImageData || this.imageData,
|
||||
cropData = $.extend({}, emulateCropData || this.cropData), // Clone one to avoid changing it
|
||||
aspectRatio = cropData.width / cropData.height,
|
||||
canvasWidth = containerWidth,
|
||||
scaledRatio;
|
||||
|
||||
if (canvasWidth == 0 || imageData.naturalWidth == 0 || imageData.naturalHeight == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (containerHeight * aspectRatio <= containerWidth) {
|
||||
canvasWidth = containerHeight * aspectRatio;
|
||||
}
|
||||
|
||||
scaledRatio = cropData.width / canvasWidth;
|
||||
|
||||
$target.css({
|
||||
maxWidth: imageData.naturalWidth / scaledRatio,
|
||||
maxHeight: imageData.naturalHeight / scaledRatio
|
||||
});
|
||||
|
||||
this.center($target);
|
||||
},
|
||||
|
||||
center: function($target, reset) {
|
||||
$target.each(function() {
|
||||
let $this = $(this),
|
||||
$canvas = $this.parent(),
|
||||
$container = $canvas.parent();
|
||||
|
||||
function center() {
|
||||
let containerHeight = $container.height(),
|
||||
canvasHeight = $canvas.height(),
|
||||
marginTop = 'auto';
|
||||
|
||||
if (canvasHeight < containerHeight) {
|
||||
marginTop = (containerHeight - canvasHeight) / 2;
|
||||
}
|
||||
|
||||
$canvas.css('margin-top', marginTop);
|
||||
}
|
||||
|
||||
if (reset) {
|
||||
$canvas.add($this).removeAttr('style');
|
||||
}
|
||||
|
||||
if (this.complete) {
|
||||
center.call(this);
|
||||
} else {
|
||||
this.onload = center;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
autoCrop: function(url, data) {
|
||||
let cropData = this.cropData,
|
||||
cropOptions = this.data[this.options.key],
|
||||
_this = this;
|
||||
|
||||
this.$list
|
||||
.find('img')
|
||||
.not(this.$target)
|
||||
.each(function() {
|
||||
let $this = $(this),
|
||||
sizeName = $this.data('sizeName');
|
||||
|
||||
if ($.inArray(sizeName, data) > -1) {
|
||||
cropOptions[sizeName] = $.extend({}, cropData);
|
||||
|
||||
if (url) {
|
||||
_this.center($this.attr('src', url), true);
|
||||
} else {
|
||||
_this.preview($this);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (!isSVG) {
|
||||
this.unbind();
|
||||
this.unbuild();
|
||||
}
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorCropper.DEFAULTS = {
|
||||
parent: false,
|
||||
output: false,
|
||||
list: false,
|
||||
key: 'data',
|
||||
data: null,
|
||||
text: {
|
||||
title: 'Crop the image',
|
||||
ok: 'OK',
|
||||
cancel: 'Cancel'
|
||||
}
|
||||
};
|
||||
|
||||
QorCropper.TOGGLE = `<div class="qor-cropper__toggle">
|
||||
<div class="qor-cropper__toggle--crop"><i class="material-icons">crop</i></div>
|
||||
<div class="qor-cropper__toggle--delete"><i class="material-icons">delete</i></div>
|
||||
</div>`;
|
||||
|
||||
QorCropper.ALERT = `<div class="qor-fieldset__alert">
|
||||
<button class="mdl-button mdl-button--accent qor-fieldset__undo" type="button">Undo delete</button>
|
||||
</div>`;
|
||||
|
||||
QorCropper.CANVAS = '<div class="qor-cropper__canvas"></div>';
|
||||
QorCropper.LIST = '<ul><li><img></li></ul>';
|
||||
QorCropper.FILE_LIST = `<div class="qor-file__list-item">
|
||||
<span><span>{{filename}}</span></span>
|
||||
<div class="qor-cropper__toggle">
|
||||
<div class="qor-cropper__toggle--delete"><i class="material-icons">delete</i></div>
|
||||
</div>
|
||||
</div>`;
|
||||
QorCropper.MODAL = `<div class="qor-modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="mdl-card mdl-shadow--2dp" role="document">
|
||||
<div class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">$[title]</h2>
|
||||
</div>
|
||||
<div class="mdl-card__supporting-text">
|
||||
<div class="qor-cropper__wrapper"></div>
|
||||
<div class="qor-cropper__options">
|
||||
<p>Sync cropping result to: <label><input type="checkbox" class="qor-cropper__options-toggle" checked/> All</label></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mdl-card__actions mdl-card--border">
|
||||
<a class="mdl-button mdl-button--colored mdl-button--raised qor-cropper__save">$[ok]</a>
|
||||
<a class="mdl-button mdl-button--colored" data-dismiss="modal">$[cancel]</a>
|
||||
</div>
|
||||
<div class="mdl-card__menu">
|
||||
<button class="mdl-button mdl-button--icon" data-dismiss="modal" aria-label="close">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
QorCropper.plugin = function(option) {
|
||||
return this.each(function() {
|
||||
let $this = $(this),
|
||||
data = $this.data(NAMESPACE),
|
||||
options,
|
||||
fn;
|
||||
|
||||
if (!data) {
|
||||
if (!$.fn.cropper) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (/destroy/.test(option)) {
|
||||
return;
|
||||
}
|
||||
|
||||
options = $.extend(true, {}, $this.data(), typeof option === 'object' && option);
|
||||
$this.data(NAMESPACE, (data = new QorCropper(this, options)));
|
||||
}
|
||||
|
||||
if (typeof option === 'string' && $.isFunction((fn = data[option]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
let selector = '.qor-file__input',
|
||||
options = {
|
||||
parent: '.qor-file',
|
||||
output: '.qor-file__options',
|
||||
list: '.qor-file__list',
|
||||
key: 'CropOptions'
|
||||
};
|
||||
|
||||
$(document)
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorCropper.plugin.call($(selector, e.target), options);
|
||||
})
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorCropper.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorCropper;
|
||||
});
|
||||
242
app/views/qor/assets/javascripts/qor/qor-datepicker.js
Normal file
242
app/views/qor/assets/javascripts/qor/qor-datepicker.js
Normal file
@@ -0,0 +1,242 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
let NAMESPACE = 'qor.datepicker',
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_CHANGE = 'pick.' + NAMESPACE,
|
||||
EVENT_CLICK = 'click.' + NAMESPACE,
|
||||
CLASS_EMBEDDED = '.qor-datepicker__embedded',
|
||||
CLASS_SAVE = '.qor-datepicker__save',
|
||||
CLASS_PARENT = '[data-picker-type]';
|
||||
|
||||
function replaceText(str, data) {
|
||||
if (typeof str === 'string') {
|
||||
if (typeof data === 'object') {
|
||||
$.each(data, function(key, val) {
|
||||
str = str.replace('$[' + String(key).toLowerCase() + ']', val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function QorDatepicker(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend(true, {}, QorDatepicker.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.date = null;
|
||||
this.formatDate = null;
|
||||
this.built = false;
|
||||
this.pickerData = this.$element.data();
|
||||
this.$parent = this.$element.closest(CLASS_PARENT);
|
||||
this.isDateTimePicker = this.$parent.data('picker-type') == 'datetime';
|
||||
this.$targetInput = this.$parent.find(this.pickerData.targetInput || (this.isDateTimePicker ? '.qor-datetimepicker__input' : '.qor-datepicker__input'));
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorDatepicker.prototype = {
|
||||
init: function() {
|
||||
if (this.$targetInput.is(':disabled')) {
|
||||
this.$element.remove();
|
||||
return;
|
||||
}
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$element.on(EVENT_CLICK, $.proxy(this.show, this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$element.off(EVENT_CLICK, this.show);
|
||||
},
|
||||
|
||||
build: function() {
|
||||
let $modal,
|
||||
$ele = this.$element,
|
||||
$targetInput = this.$targetInput,
|
||||
defaultDate = $targetInput.val(),
|
||||
datepickerOptions = {
|
||||
date: new Date(),
|
||||
inline: true
|
||||
};
|
||||
|
||||
if (this.built) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($ele.is(':input') && Date.parse($ele.val())) {
|
||||
datepickerOptions.date = new Date($ele.val());
|
||||
} else if (defaultDate && Date.parse(defaultDate)) {
|
||||
datepickerOptions.date = new Date(defaultDate);
|
||||
}
|
||||
|
||||
this.$modal = $modal = $(replaceText(QorDatepicker.TEMPLATE, this.options.text)).appendTo('body');
|
||||
|
||||
if ($targetInput.data('start-date')) {
|
||||
datepickerOptions.startDate = new Date($targetInput.data('start-date'));
|
||||
}
|
||||
|
||||
if ($targetInput.data('end-date')) {
|
||||
datepickerOptions.endDate = new Date($targetInput.data('end-date'));
|
||||
}
|
||||
|
||||
|
||||
$modal
|
||||
.find(CLASS_EMBEDDED)
|
||||
.on(EVENT_CHANGE, $.proxy(this.change, this))
|
||||
.qorDatepicker(datepickerOptions)
|
||||
.triggerHandler(EVENT_CHANGE);
|
||||
|
||||
$modal.find(CLASS_SAVE).on(EVENT_CLICK, $.proxy(this.pick, this));
|
||||
|
||||
this.built = true;
|
||||
},
|
||||
|
||||
unbuild: function() {
|
||||
if (!this.built) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$modal
|
||||
.find(CLASS_EMBEDDED)
|
||||
.off(EVENT_CHANGE, this.change)
|
||||
.qorDatepicker('destroy')
|
||||
.end()
|
||||
.find(CLASS_SAVE)
|
||||
.off(EVENT_CLICK, this.pick)
|
||||
.end()
|
||||
.remove();
|
||||
},
|
||||
|
||||
change: function(e) {
|
||||
var $modal = this.$modal;
|
||||
var $target = $(e.target);
|
||||
var date;
|
||||
|
||||
this.date = date = $target.qorDatepicker('getDate');
|
||||
this.formatDate = $target.qorDatepicker('getDate', true);
|
||||
|
||||
$modal.find('.qor-datepicker__picked-year').text(date.getFullYear());
|
||||
$modal
|
||||
.find('.qor-datepicker__picked-date')
|
||||
.text(
|
||||
[$target.qorDatepicker('getDayName', date.getDay(), true) + ',', String($target.qorDatepicker('getMonthName', date.getMonth(), true)), date.getDate()].join(' ')
|
||||
);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (!this.built) {
|
||||
this.build();
|
||||
}
|
||||
|
||||
this.$modal.qorModal('show');
|
||||
},
|
||||
|
||||
pick: function() {
|
||||
let $targetInput = this.$targetInput,
|
||||
newValue = this.formatDate;
|
||||
|
||||
if (this.isDateTimePicker) {
|
||||
var regDate = /^\d{4}-\d{1,2}-\d{1,2}/;
|
||||
var oldValue = $targetInput.val();
|
||||
var hasDate = regDate.test(oldValue);
|
||||
|
||||
if (hasDate) {
|
||||
newValue = oldValue.replace(regDate, newValue);
|
||||
} else {
|
||||
newValue = newValue + ' 00:00';
|
||||
}
|
||||
}
|
||||
|
||||
$targetInput.val(newValue).trigger('change');
|
||||
this.$modal.qorModal('hide');
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.unbuild();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorDatepicker.DEFAULTS = {
|
||||
text: {
|
||||
title: 'Pick a date',
|
||||
ok: 'OK',
|
||||
cancel: 'Cancel'
|
||||
}
|
||||
};
|
||||
|
||||
QorDatepicker.TEMPLATE = `<div class="qor-modal fade qor-datepicker" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="mdl-card mdl-shadow--2dp" role="document">
|
||||
<div class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">$[title]</h2>
|
||||
</div>
|
||||
<div class="mdl-card__supporting-text">
|
||||
<div class="qor-datepicker__picked">
|
||||
<div class="qor-datepicker__picked-year"></div>
|
||||
<div class="qor-datepicker__picked-date"></div>
|
||||
</div>
|
||||
<div class="qor-datepicker__embedded"></div>
|
||||
</div>
|
||||
<div class="mdl-card__actions">
|
||||
<a class="mdl-button mdl-button--colored mdl-button--raised qor-datepicker__save">$[ok]</a>
|
||||
<a class="mdl-button mdl-button--colored " data-dismiss="modal">$[cancel]</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
QorDatepicker.plugin = function(option) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var options;
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (!$.fn.qorDatepicker) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (/destroy/.test(option)) {
|
||||
return;
|
||||
}
|
||||
|
||||
options = $.extend(true, {}, $this.data(), typeof option === 'object' && option);
|
||||
$this.data(NAMESPACE, (data = new QorDatepicker(this, options)));
|
||||
}
|
||||
|
||||
if (typeof option === 'string' && $.isFunction((fn = data[option]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var selector = '[data-toggle="qor.datepicker"]';
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorDatepicker.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorDatepicker.plugin.call($(selector, e.target));
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorDatepicker;
|
||||
});
|
||||
106
app/views/qor/assets/javascripts/qor/qor-dirtyform.js
Normal file
106
app/views/qor/assets/javascripts/qor/qor-dirtyform.js
Normal file
@@ -0,0 +1,106 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
var dirtyForm = function(ele, options) {
|
||||
var hasChangedObj = false;
|
||||
|
||||
if (this instanceof jQuery) {
|
||||
options = ele;
|
||||
ele = this;
|
||||
} else if (!(ele instanceof jQuery)) {
|
||||
ele = $(ele);
|
||||
}
|
||||
|
||||
ele.each(function(item, element) {
|
||||
var $ele = $(element);
|
||||
|
||||
if ($ele.is('form')) {
|
||||
if ($ele.hasClass('ignore-dirtyform')) {
|
||||
return false;
|
||||
}
|
||||
hasChangedObj = dirtyForm(
|
||||
$ele.find(
|
||||
'input:not([type="hidden"]):not(".search-field input"):not(".chosen-search input"):not(".ignore-dirtyform"), textarea, select'
|
||||
),
|
||||
options
|
||||
);
|
||||
if (hasChangedObj) {
|
||||
return false;
|
||||
}
|
||||
} else if ($ele.is(':checkbox') || $ele.is(':radio')) {
|
||||
if ($ele.hasClass('ignore-dirtyform')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (element.checked != element.defaultChecked) {
|
||||
hasChangedObj = true;
|
||||
return false;
|
||||
}
|
||||
} else if ($ele.is('input') || $ele.is('textarea')) {
|
||||
if ($ele.hasClass('ignore-dirtyform')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (element.value != element.defaultValue) {
|
||||
hasChangedObj = true;
|
||||
return false;
|
||||
}
|
||||
} else if ($ele.is('select')) {
|
||||
if ($ele.hasClass('ignore-dirtyform')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var option;
|
||||
var defaultSelectedIndex = 0;
|
||||
var numberOfOptions = element.options.length;
|
||||
|
||||
for (var i = 0; i < numberOfOptions; i++) {
|
||||
option = element.options[i];
|
||||
hasChangedObj = hasChangedObj || option.selected != option.defaultSelected;
|
||||
if (option.defaultSelected) {
|
||||
defaultSelectedIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasChangedObj && !element.multiple) {
|
||||
hasChangedObj = defaultSelectedIndex != element.selectedIndex;
|
||||
}
|
||||
|
||||
if (hasChangedObj) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return hasChangedObj;
|
||||
};
|
||||
|
||||
$.fn.extend({
|
||||
dirtyForm: dirtyForm
|
||||
});
|
||||
|
||||
$(function() {
|
||||
$(document).on('submit', 'form', function() {
|
||||
$.fn.qorSlideoutBeforeHide = null;
|
||||
});
|
||||
|
||||
$(document).on('change', 'form', function() {
|
||||
if ($(this).dirtyForm()) {
|
||||
$.fn.qorSlideoutBeforeHide = true;
|
||||
} else {
|
||||
$.fn.qorSlideoutBeforeHide = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
302
app/views/qor/assets/javascripts/qor/qor-filter-time.js
Normal file
302
app/views/qor/assets/javascripts/qor/qor-filter-time.js
Normal file
@@ -0,0 +1,302 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
let location = window.location,
|
||||
$document = $(document),
|
||||
NAMESPACE = 'qor.filter',
|
||||
EVENT_FILTER_CHANGE = 'filterChanged.' + NAMESPACE,
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_CLICK = 'click.' + NAMESPACE,
|
||||
CLASS_BOTTOMSHEETS = '.qor-bottomsheets',
|
||||
CLASS_DATE_START = '.qor-filter__start',
|
||||
CLASS_DATE_END = '.qor-filter__end',
|
||||
CLASS_SEARCH_PARAM = '[data-search-param]',
|
||||
CLASS_FILTER_SELECTOR = '.qor-filter__dropdown',
|
||||
CLASS_FILTER_TOGGLE = '.qor-filter-toggle',
|
||||
CLASS_IS_SELECTED = 'is-selected';
|
||||
|
||||
function QorFilterTime(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorFilterTime.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorFilterTime.prototype = {
|
||||
constructor: QorFilterTime,
|
||||
|
||||
init: function() {
|
||||
this.bind();
|
||||
let $element = this.$element,
|
||||
lcoal_moment = window.moment();
|
||||
|
||||
this.$timeStart = $element.find(CLASS_DATE_START);
|
||||
this.$timeEnd = $element.find(CLASS_DATE_END);
|
||||
this.$searchParam = $element.find(CLASS_SEARCH_PARAM);
|
||||
this.$searchButton = $element.find(this.options.button);
|
||||
|
||||
this.startWeekDate = lcoal_moment.startOf('isoweek').toDate();
|
||||
this.endWeekDate = lcoal_moment.endOf('isoweek').toDate();
|
||||
|
||||
this.startMonthDate = lcoal_moment.startOf('month').toDate();
|
||||
this.endMonthDate = lcoal_moment.endOf('month').toDate();
|
||||
this.initActionTemplate();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
var options = this.options;
|
||||
|
||||
this.$element
|
||||
.on(EVENT_CLICK, options.trigger, this.show.bind(this))
|
||||
.on(EVENT_CLICK, options.label, this.setFilterTime.bind(this))
|
||||
.on(EVENT_CLICK, options.clear, this.clear.bind(this))
|
||||
.on(EVENT_CLICK, options.button, this.search.bind(this));
|
||||
|
||||
$document.on(EVENT_CLICK, this.close);
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$element.off(EVENT_CLICK);
|
||||
},
|
||||
|
||||
initActionTemplate: function() {
|
||||
let paramFrom = this.$element.data('schedule-from');
|
||||
let paramTo = this.$element.data('schedule-to');
|
||||
|
||||
|
||||
let scheduleStartAt = this.getUrlParameter(paramFrom || 'schedule_start_at');
|
||||
let scheduleEndAt = this.getUrlParameter(paramTo || 'schedule_end_at');
|
||||
let $filterToggle = $(this.options.trigger);
|
||||
|
||||
if (scheduleStartAt || scheduleEndAt) {
|
||||
this.$timeStart.val(scheduleStartAt);
|
||||
this.$timeEnd.val(scheduleEndAt);
|
||||
|
||||
scheduleEndAt = !scheduleEndAt ? '' : ' - ' + scheduleEndAt;
|
||||
$filterToggle
|
||||
.addClass('active clearable')
|
||||
.find('.qor-selector-label')
|
||||
.html(scheduleStartAt + scheduleEndAt);
|
||||
$filterToggle.append('<i class="material-icons qor-selector-clear">clear</i>');
|
||||
}
|
||||
},
|
||||
|
||||
show: function() {
|
||||
this.$element.find(CLASS_FILTER_SELECTOR).toggle();
|
||||
},
|
||||
|
||||
close: function(e) {
|
||||
var $target = $(e.target),
|
||||
$filter = $(CLASS_FILTER_SELECTOR),
|
||||
filterVisible = $filter.is(':visible'),
|
||||
isInFilter = $target.closest(CLASS_FILTER_SELECTOR).length,
|
||||
isInToggle = $target.closest(CLASS_FILTER_TOGGLE).length,
|
||||
isInModal = $target.closest('.qor-modal').length,
|
||||
isInTimePicker = $target.closest('.ui-timepicker-wrapper').length;
|
||||
|
||||
if (filterVisible && (isInFilter || isInToggle || isInModal || isInTimePicker)) {
|
||||
return;
|
||||
}
|
||||
$filter.hide();
|
||||
},
|
||||
|
||||
setFilterTime: function(e) {
|
||||
let $target = $(e.target),
|
||||
data = $target.data(),
|
||||
range = data.filterRange,
|
||||
startTime,
|
||||
endTime,
|
||||
startDate,
|
||||
endDate;
|
||||
|
||||
if (!range) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$(this.options.label).removeClass(CLASS_IS_SELECTED);
|
||||
$target.addClass(CLASS_IS_SELECTED);
|
||||
|
||||
if (range == 'events') {
|
||||
this.$timeStart.val(data.scheduleStartAt || '');
|
||||
this.$timeEnd.val(data.scheduleEndAt || '');
|
||||
this.$searchButton.click();
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (range) {
|
||||
case 'today':
|
||||
startDate = endDate = new Date();
|
||||
break;
|
||||
case 'week':
|
||||
startDate = this.startWeekDate;
|
||||
endDate = this.endWeekDate;
|
||||
break;
|
||||
case 'month':
|
||||
startDate = this.startMonthDate;
|
||||
endDate = this.endMonthDate;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!startDate || !endDate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
startTime = this.getTime(startDate) + ' 00:00';
|
||||
endTime = this.getTime(endDate) + ' 23:59';
|
||||
|
||||
this.$timeStart.val(startTime);
|
||||
this.$timeEnd.val(endTime);
|
||||
this.$searchButton.click();
|
||||
},
|
||||
|
||||
getTime: function(dateNow) {
|
||||
var month = dateNow.getMonth() + 1,
|
||||
date = dateNow.getDate();
|
||||
|
||||
month = month < 10 ? '0' + month : month;
|
||||
date = date < 10 ? '0' + date : date;
|
||||
|
||||
return dateNow.getFullYear() + '-' + month + '-' + date;
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
var $trigger = $(this.options.trigger),
|
||||
$label = $trigger.find('.qor-selector-label');
|
||||
|
||||
$trigger.removeClass('active clearable');
|
||||
$label.html($label.data('label'));
|
||||
this.$timeStart.val('');
|
||||
this.$timeEnd.val('');
|
||||
|
||||
this.$searchButton.click();
|
||||
return false;
|
||||
},
|
||||
|
||||
getUrlParameter: function(name) {
|
||||
let search = decodeURIComponent(location.search),
|
||||
parameterName = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]'),
|
||||
regex = new RegExp('[\\?&]' + parameterName + '=([^&#]*)'),
|
||||
results = regex.exec(search);
|
||||
|
||||
return results === null ? '' : results[1].replace(/\+/g, ' ');
|
||||
},
|
||||
|
||||
updateQueryStringParameter: function(key, value, url) {
|
||||
let href = url || location.href,
|
||||
local_hash = href.match(/#\S*$/) || '',
|
||||
escapedkey = String(key).replace(/[\\^$*+?.()|[\]{}]/g, '\\$&'),
|
||||
re = new RegExp('([?&])' + escapedkey + '=.*?(&|$)', 'i'),
|
||||
separator = href.indexOf('?') !== -1 ? '&' : '?';
|
||||
|
||||
if (local_hash) {
|
||||
local_hash = local_hash[0];
|
||||
href = href.replace(local_hash, '');
|
||||
}
|
||||
|
||||
if (href.match(re)) {
|
||||
if (value) {
|
||||
href = href.replace(re, '$1' + key + '=' + value + '$2');
|
||||
} else {
|
||||
if (RegExp.$1 === '?' || RegExp.$1 === RegExp.$2) {
|
||||
href = href.replace(re, '$1');
|
||||
} else {
|
||||
href = href.replace(re, '');
|
||||
}
|
||||
}
|
||||
} else if (value) {
|
||||
href = href + separator + key + '=' + value;
|
||||
}
|
||||
|
||||
return href + local_hash;
|
||||
},
|
||||
|
||||
search: function() {
|
||||
var $searchParam = this.$searchParam,
|
||||
href = location.href,
|
||||
_this = this,
|
||||
type = 'qor.filter.time';
|
||||
|
||||
if (!$searchParam.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
$searchParam.each(function() {
|
||||
var $this = $(this),
|
||||
searchParam = $this.data().searchParam,
|
||||
val = $this.val();
|
||||
|
||||
href = _this.updateQueryStringParameter(searchParam, val, href);
|
||||
});
|
||||
|
||||
if (this.$element.closest(CLASS_BOTTOMSHEETS).length) {
|
||||
$(CLASS_BOTTOMSHEETS).trigger(EVENT_FILTER_CHANGE, [href, type]);
|
||||
} else {
|
||||
location.href = href;
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorFilterTime.DEFAULTS = {
|
||||
label: false,
|
||||
trigger: false,
|
||||
button: false,
|
||||
clear: false
|
||||
};
|
||||
|
||||
QorFilterTime.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorFilterTime(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var selector = '[data-toggle="qor.filter.time"]';
|
||||
var options = {
|
||||
label: '.qor-filter__block-buttons button',
|
||||
trigger: 'a.qor-filter-toggle',
|
||||
button: '.qor-filter__button-search',
|
||||
clear: '.qor-selector-clear'
|
||||
};
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorFilterTime.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorFilterTime.plugin.call($(selector, e.target), options);
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorFilterTime;
|
||||
});
|
||||
237
app/views/qor/assets/javascripts/qor/qor-filter.js
Normal file
237
app/views/qor/assets/javascripts/qor/qor-filter.js
Normal file
@@ -0,0 +1,237 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
let location = window.location,
|
||||
NAMESPACE = 'qor.filter',
|
||||
EVENT_FILTER_CHANGE = 'filterChanged.' + NAMESPACE,
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_CLICK = 'click.' + NAMESPACE,
|
||||
EVENT_CHANGE = 'change.' + NAMESPACE,
|
||||
CLASS_IS_ACTIVE = 'is-active',
|
||||
CLASS_BOTTOMSHEETS = '.qor-bottomsheets';
|
||||
|
||||
function encodeSearch(data, detached) {
|
||||
var search = decodeURI(location.search);
|
||||
var per_page = location.search.match(/per_page=\d+/);
|
||||
var params;
|
||||
|
||||
search = search.replace(/per_page=\d+/g,'').replace(/page=\d+/,'page=1');
|
||||
|
||||
|
||||
if(per_page && per_page.length){
|
||||
search = search + "&" + per_page[0];
|
||||
}
|
||||
|
||||
|
||||
if ($.isArray(data)) {
|
||||
params = decodeSearch(search);
|
||||
|
||||
$.each(data, function(i, param) {
|
||||
i = $.inArray(param, params);
|
||||
|
||||
if (i === -1) {
|
||||
params.push(param);
|
||||
} else if (detached) {
|
||||
params.splice(i, 1);
|
||||
}
|
||||
});
|
||||
|
||||
search = '?' + params.join('&');
|
||||
}
|
||||
|
||||
return search;
|
||||
}
|
||||
|
||||
function decodeSearch(search) {
|
||||
var data = [];
|
||||
|
||||
if (search && search.indexOf('?') > -1) {
|
||||
search = search.replace(/\+/g, ' ').split('?')[1];
|
||||
|
||||
if (search && search.indexOf('#') > -1) {
|
||||
search = search.split('#')[0];
|
||||
}
|
||||
|
||||
if (search) {
|
||||
// search = search.toLowerCase();
|
||||
data = $.map(search.split('&'), function(n) {
|
||||
var param = [];
|
||||
var value;
|
||||
|
||||
n = n.split('=');
|
||||
value = n[1];
|
||||
param.push(n[0]);
|
||||
|
||||
if (value) {
|
||||
value = $.trim(decodeURIComponent(value));
|
||||
|
||||
if (value) {
|
||||
param.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
return param.join('=');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function QorFilter(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorFilter.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorFilter.prototype = {
|
||||
constructor: QorFilter,
|
||||
|
||||
init: function() {
|
||||
// this.parse();
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
var options = this.options;
|
||||
|
||||
this.$element.on(EVENT_CLICK, options.label, $.proxy(this.toggle, this)).on(EVENT_CHANGE, options.group, $.proxy(this.toggle, this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$element.off(EVENT_CLICK, this.toggle).off(EVENT_CHANGE, this.toggle);
|
||||
},
|
||||
|
||||
toggle: function(e) {
|
||||
let $target = $(e.currentTarget),
|
||||
data = [],
|
||||
params,
|
||||
param,
|
||||
search,
|
||||
name,
|
||||
value,
|
||||
index,
|
||||
matched,
|
||||
paramName;
|
||||
|
||||
if ($target.is('select')) {
|
||||
params = decodeSearch(decodeURI(location.search));
|
||||
|
||||
paramName = name = $target.attr('name');
|
||||
value = $target.val();
|
||||
param = [name];
|
||||
|
||||
if (value) {
|
||||
param.push(value);
|
||||
}
|
||||
|
||||
param = param.join('=');
|
||||
|
||||
if (value) {
|
||||
data.push(param);
|
||||
}
|
||||
|
||||
$target.children().each(function() {
|
||||
var $this = $(this);
|
||||
var param = [name];
|
||||
var value = $.trim($this.prop('value'));
|
||||
|
||||
if (value) {
|
||||
param.push(value);
|
||||
}
|
||||
|
||||
param = param.join('=');
|
||||
index = $.inArray(param, params);
|
||||
|
||||
if (index > -1) {
|
||||
matched = param;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (matched) {
|
||||
data.push(matched);
|
||||
search = encodeSearch(data, true);
|
||||
} else {
|
||||
search = encodeSearch(data);
|
||||
}
|
||||
} else if ($target.is('a')) {
|
||||
e.preventDefault();
|
||||
paramName = $target.data().paramName;
|
||||
data = decodeSearch($target.attr('href'));
|
||||
if ($target.hasClass(CLASS_IS_ACTIVE)) {
|
||||
search = encodeSearch(data, true); // set `true` to detach
|
||||
} else {
|
||||
search = encodeSearch(data);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.$element.closest(CLASS_BOTTOMSHEETS).length) {
|
||||
$(CLASS_BOTTOMSHEETS).trigger(EVENT_FILTER_CHANGE, [search, paramName]);
|
||||
} else {
|
||||
location.search = search;
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorFilter.DEFAULTS = {
|
||||
label: false,
|
||||
group: false
|
||||
};
|
||||
|
||||
QorFilter.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorFilter(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var selector = '[data-toggle="qor.filter"]';
|
||||
var options = {
|
||||
label: 'a',
|
||||
group: 'select'
|
||||
};
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorFilter.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorFilter.plugin.call($(selector, e.target), options);
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorFilter;
|
||||
});
|
||||
169
app/views/qor/assets/javascripts/qor/qor-fixer.js
Normal file
169
app/views/qor/assets/javascripts/qor/qor-fixer.js
Normal file
@@ -0,0 +1,169 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
let $window = $(window),
|
||||
NAMESPACE = 'qor.fixer',
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_RESIZE = 'resize.' + NAMESPACE,
|
||||
EVENT_SCROLL = 'scroll.' + NAMESPACE,
|
||||
CLASS_FIXED_TABLE = 'qor-table-fixed-header',
|
||||
CLASS_HEADER = '.qor-page__header';
|
||||
|
||||
function QorFixer(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorFixer.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorFixer.prototype = {
|
||||
constructor: QorFixer,
|
||||
|
||||
init: function() {
|
||||
var options = this.options;
|
||||
var $this = this.$element;
|
||||
if (this.isNeedBuild()) {
|
||||
return;
|
||||
}
|
||||
this.$thead = $this.find('> thead');
|
||||
this.$tbody = $this.find('> tbody');
|
||||
|
||||
this.$header = $(options.header);
|
||||
this.$subHeader = $(options.subHeader);
|
||||
this.$content = $(options.content);
|
||||
this.marginBottomPX = parseInt(this.$subHeader.css('marginBottom'));
|
||||
this.paddingHeight = options.paddingHeight;
|
||||
|
||||
this.resize();
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$content.on(EVENT_SCROLL, this.toggle.bind(this));
|
||||
$window.on(EVENT_RESIZE, this.resize.bind(this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$content.off(EVENT_SCROLL, this.toggle).off(EVENT_RESIZE, this.resize);
|
||||
},
|
||||
|
||||
isNeedBuild: function() {
|
||||
var $this = this.$element;
|
||||
// disable fixer if have multiple tables or in search page or in media library list page
|
||||
if (
|
||||
$('.qor-page__body .qor-js-table').length > 1 ||
|
||||
$('.qor-global-search--container').length > 0 ||
|
||||
$this.hasClass('qor-table--medialibrary') ||
|
||||
$this.is(':hidden') ||
|
||||
$this.find('tbody > tr:visible').length <= 1 ||
|
||||
$this.data("disable-fixer")
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
build: function() {
|
||||
let headerWidth = [],
|
||||
$items = this.$tbody.find('> tr:first').children();
|
||||
|
||||
$items.each(function() {
|
||||
let tdWidth = $(this).outerWidth();
|
||||
$(this).outerWidth(tdWidth);
|
||||
headerWidth.push(tdWidth);
|
||||
});
|
||||
|
||||
this.$thead
|
||||
.find('>tr')
|
||||
.children()
|
||||
.each(function(i) {
|
||||
$(this).outerWidth(headerWidth[i]);
|
||||
});
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (!this.$content.length) {
|
||||
return;
|
||||
}
|
||||
let $element = this.$element,
|
||||
$thead = this.$thead,
|
||||
scrollTop = this.$content.scrollTop(),
|
||||
offsetTop = this.$subHeader.outerHeight() + this.paddingHeight + this.marginBottomPX,
|
||||
headerHeight = $('.qor-page__header').outerHeight(),
|
||||
pageTop = this.$content.offset().top + $(CLASS_HEADER).height();
|
||||
|
||||
if (scrollTop > offsetTop - headerHeight) {
|
||||
$thead.css({top: pageTop});
|
||||
$element.addClass(CLASS_FIXED_TABLE);
|
||||
} else {
|
||||
$element.removeClass(CLASS_FIXED_TABLE);
|
||||
}
|
||||
},
|
||||
|
||||
resize: function() {
|
||||
this.build();
|
||||
this.toggle();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
if (this.buildCheck()) {
|
||||
return;
|
||||
}
|
||||
this.unbind();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorFixer.DEFAULTS = {
|
||||
header: false,
|
||||
content: false
|
||||
};
|
||||
|
||||
QorFixer.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
$this.data(NAMESPACE, (data = new QorFixer(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
|
||||
fn.call(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var selector = '.qor-js-table';
|
||||
var options = {
|
||||
header: '.mdl-layout__header',
|
||||
subHeader: '.qor-page__header',
|
||||
content: '.mdl-layout__content',
|
||||
paddingHeight: 2 // Fix sub header height bug
|
||||
};
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorFixer.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorFixer.plugin.call($(selector, e.target), options);
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorFixer;
|
||||
});
|
||||
202
app/views/qor/assets/javascripts/qor/qor-inline-edit.js
Normal file
202
app/views/qor/assets/javascripts/qor/qor-inline-edit.js
Normal file
@@ -0,0 +1,202 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
const NAMESPACE = 'qor.inlineEdit',
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_CLICK = 'click.' + NAMESPACE,
|
||||
EVENT_MOUSEENTER = 'mouseenter.' + NAMESPACE,
|
||||
EVENT_MOUSELEAVE = 'mouseleave.' + NAMESPACE,
|
||||
CLASS_FIELD = '.qor-field',
|
||||
CLASS_FIELD_SHOW = '.qor-field__show',
|
||||
CLASS_FIELD_SHOW_INNER = '.qor-field__show-inner',
|
||||
CLASS_EDIT = '.qor-inlineedit__edit',
|
||||
CLASS_SAVE = '.qor-inlineedit__save',
|
||||
CLASS_BUTTONS = '.qor-inlineedit__buttons',
|
||||
CLASS_CANCEL = '.qor-inlineedit__cancel',
|
||||
CLASS_CONTAINER = 'qor-inlineedit__field';
|
||||
|
||||
function QorInlineEdit(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorInlineEdit.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
function getJsonData(names, data) {
|
||||
let key,
|
||||
value = data[names[0].slice(1)];
|
||||
|
||||
if (names.length > 1) {
|
||||
for (let i = 1; i < names.length; i++) {
|
||||
key = names[i].slice(1);
|
||||
value = $.isArray(value) ? value[0][key] : value[key];
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
QorInlineEdit.prototype = {
|
||||
constructor: QorInlineEdit,
|
||||
|
||||
init: function() {
|
||||
let $element = this.$element,
|
||||
saveButton = $element.data('button-save'),
|
||||
cancelButton = $element.data('button-cancel');
|
||||
|
||||
this.TEMPLATE_SAVE = `<div class="qor-inlineedit__buttons">
|
||||
<button class="mdl-button mdl-button--colored mdl-js-button qor-button--small qor-inlineedit__cancel" type="button">${cancelButton}</button>
|
||||
<button class="mdl-button mdl-button--colored mdl-js-button qor-button--small qor-inlineedit__save" type="button">${saveButton}</button>
|
||||
</div>`;
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$element
|
||||
.on(EVENT_MOUSEENTER, CLASS_FIELD_SHOW, this.showEditButton)
|
||||
.on(EVENT_MOUSELEAVE, CLASS_FIELD_SHOW, this.hideEditButton)
|
||||
.on(EVENT_CLICK, CLASS_CANCEL, this.hideEdit)
|
||||
.on(EVENT_CLICK, CLASS_SAVE, this.saveEdit)
|
||||
.on(EVENT_CLICK, CLASS_EDIT, this.showEdit.bind(this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$element
|
||||
.off(EVENT_MOUSEENTER)
|
||||
.off(EVENT_MOUSELEAVE)
|
||||
.off(EVENT_CLICK);
|
||||
},
|
||||
|
||||
showEditButton: function(e) {
|
||||
let $edit = $(QorInlineEdit.TEMPLATE_EDIT),
|
||||
$field = $(e.target).closest(CLASS_FIELD);
|
||||
|
||||
if ($field.find('input:disabled, textarea:disabled,select:disabled').length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$edit.appendTo($(this));
|
||||
},
|
||||
|
||||
hideEditButton: function() {
|
||||
$('.qor-inlineedit__edit').remove();
|
||||
},
|
||||
|
||||
showEdit: function(e) {
|
||||
let $parent = $(e.target)
|
||||
.closest(CLASS_EDIT)
|
||||
.hide()
|
||||
.closest(CLASS_FIELD)
|
||||
.addClass(CLASS_CONTAINER),
|
||||
$save = $(this.TEMPLATE_SAVE);
|
||||
|
||||
$save.appendTo($parent);
|
||||
},
|
||||
|
||||
hideEdit: function() {
|
||||
let $parent = $(this)
|
||||
.closest(CLASS_FIELD)
|
||||
.removeClass(CLASS_CONTAINER);
|
||||
$parent.find(CLASS_BUTTONS).remove();
|
||||
},
|
||||
|
||||
saveEdit: function() {
|
||||
let $btn = $(this),
|
||||
$parent = $btn.closest(CLASS_FIELD),
|
||||
$form = $btn.closest('form'),
|
||||
$hiddenInput = $parent.closest('.qor-fieldset').find('input.qor-hidden__primary_key[type="hidden"]'),
|
||||
$input = $parent.find('input[name*="QorResource"],textarea[name*="QorResource"],select[name*="QorResource"]'),
|
||||
names = $input.length && $input.prop('name').match(/\.\w+/g),
|
||||
inputData = $input.serialize();
|
||||
|
||||
if ($hiddenInput.length) {
|
||||
inputData = `${inputData}&${$hiddenInput.serialize()}`;
|
||||
}
|
||||
|
||||
if (names.length) {
|
||||
$.ajax($form.prop('action'), {
|
||||
method: $form.prop('method'),
|
||||
data: inputData,
|
||||
dataType: 'json',
|
||||
beforeSend: function() {
|
||||
$btn.prop('disabled', true);
|
||||
},
|
||||
success: function(data) {
|
||||
let newValue = getJsonData(names, data),
|
||||
$show = $parent.removeClass(CLASS_CONTAINER).find(CLASS_FIELD_SHOW),
|
||||
$inner = $show.find(CLASS_FIELD_SHOW_INNER);
|
||||
|
||||
if (typeof newValue === 'string' || newValue instanceof String){
|
||||
newValue = newValue.escapeSymbol();
|
||||
}
|
||||
|
||||
if ($inner.length) {
|
||||
$inner.html(newValue);
|
||||
} else {
|
||||
$show.html(newValue);
|
||||
}
|
||||
|
||||
$parent.find(CLASS_BUTTONS).remove();
|
||||
$btn.prop('disabled', false);
|
||||
},
|
||||
error: function(err) {
|
||||
window.QOR.handleAjaxError(err);
|
||||
$btn.prop('disabled', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorInlineEdit.DEFAULTS = {};
|
||||
|
||||
QorInlineEdit.TEMPLATE_EDIT = `<button class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored qor-inlineedit__edit" type="button"><i class="material-icons">mode_edit</i></button>`;
|
||||
|
||||
QorInlineEdit.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
$this.data(NAMESPACE, (data = new QorInlineEdit(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
|
||||
fn.call(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
let selector = '[data-toggle="qor.inlineEdit"]',
|
||||
options = {};
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorInlineEdit.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorInlineEdit.plugin.call($(selector, e.target), options);
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorInlineEdit;
|
||||
});
|
||||
59
app/views/qor/assets/javascripts/qor/qor-material.js
Normal file
59
app/views/qor/assets/javascripts/qor/qor-material.js
Normal file
@@ -0,0 +1,59 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
let componentHandler = window.componentHandler,
|
||||
NAMESPACE = 'qor.material',
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_UPDATE = 'update.' + NAMESPACE,
|
||||
SELECTOR_COMPONENT = '[class*="mdl-js"],[class*="mdl-tooltip"]';
|
||||
|
||||
function enable(target) {
|
||||
/*jshint undef:false */
|
||||
if (componentHandler) {
|
||||
// Enable all MDL (Material Design Lite) components within the target element
|
||||
if ($(target).is(SELECTOR_COMPONENT)) {
|
||||
componentHandler.upgradeElements(target);
|
||||
} else {
|
||||
componentHandler.upgradeElements($(SELECTOR_COMPONENT, target).toArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function disable(target) {
|
||||
/*jshint undef:false */
|
||||
if (componentHandler) {
|
||||
// Destroy all MDL (Material Design Lite) components within the target element
|
||||
if ($(target).is(SELECTOR_COMPONENT)) {
|
||||
componentHandler.downgradeElements(target);
|
||||
} else {
|
||||
componentHandler.downgradeElements($(SELECTOR_COMPONENT, target).toArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$(document)
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
enable(e.target);
|
||||
})
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
disable(e.target);
|
||||
})
|
||||
.on(EVENT_UPDATE, function(e) {
|
||||
disable(e.target);
|
||||
enable(e.target);
|
||||
});
|
||||
});
|
||||
});
|
||||
235
app/views/qor/assets/javascripts/qor/qor-modal.js
Normal file
235
app/views/qor/assets/javascripts/qor/qor-modal.js
Normal file
@@ -0,0 +1,235 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
let $document = $(document),
|
||||
NAMESPACE = 'qor.modal',
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_CLICK = 'click.' + NAMESPACE,
|
||||
EVENT_KEYUP = 'keyup.' + NAMESPACE,
|
||||
EVENT_SHOW = 'show.' + NAMESPACE,
|
||||
EVENT_SHOWN = 'shown.' + NAMESPACE,
|
||||
EVENT_HIDE = 'hide.' + NAMESPACE,
|
||||
EVENT_HIDDEN = 'hidden.' + NAMESPACE,
|
||||
EVENT_TRANSITION_END = 'transitionend',
|
||||
CLASS_OPEN = 'qor-modal-open',
|
||||
CLASS_SHOWN = 'shown',
|
||||
CLASS_FADE = 'fade',
|
||||
CLASS_IN = 'in',
|
||||
ARIA_HIDDEN = 'aria-hidden';
|
||||
|
||||
function QorModal(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorModal.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.transitioning = false;
|
||||
this.fadable = false;
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorModal.prototype = {
|
||||
constructor: QorModal,
|
||||
|
||||
init: function() {
|
||||
this.fadable = this.$element.hasClass(CLASS_FADE);
|
||||
|
||||
if (this.options.show) {
|
||||
this.show();
|
||||
} else {
|
||||
this.toggle();
|
||||
}
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$element.on(EVENT_CLICK, $.proxy(this.click, this));
|
||||
|
||||
if (this.options.keyboard) {
|
||||
$document.on(EVENT_KEYUP, $.proxy(this.keyup, this));
|
||||
}
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$element.off(EVENT_CLICK, this.click);
|
||||
|
||||
if (this.options.keyboard) {
|
||||
$document.off(EVENT_KEYUP, this.keyup);
|
||||
}
|
||||
},
|
||||
|
||||
click: function(e) {
|
||||
var element = this.$element[0];
|
||||
var target = e.target;
|
||||
|
||||
if (target === element && this.options.backdrop) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
while (target !== element) {
|
||||
if ($(target).data('dismiss') === 'modal') {
|
||||
this.hide();
|
||||
break;
|
||||
}
|
||||
|
||||
target = target.parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
keyup: function(e) {
|
||||
if (e.which === 27) {
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
show: function(noTransition) {
|
||||
var $this = this.$element,
|
||||
showEvent;
|
||||
|
||||
if (this.transitioning || $this.hasClass(CLASS_IN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
showEvent = $.Event(EVENT_SHOW);
|
||||
$this.trigger(showEvent);
|
||||
|
||||
if (showEvent.isDefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$document.find('body').addClass(CLASS_OPEN);
|
||||
|
||||
/*jshint expr:true */
|
||||
$this
|
||||
.addClass(CLASS_SHOWN)
|
||||
.scrollTop(0)
|
||||
.get(0).offsetHeight; // reflow for transition
|
||||
this.transitioning = true;
|
||||
|
||||
if (noTransition || !this.fadable) {
|
||||
$this.addClass(CLASS_IN);
|
||||
this.shown();
|
||||
return;
|
||||
}
|
||||
|
||||
$this.one(EVENT_TRANSITION_END, $.proxy(this.shown, this));
|
||||
$this.addClass(CLASS_IN);
|
||||
},
|
||||
|
||||
shown: function() {
|
||||
this.transitioning = false;
|
||||
this.bind();
|
||||
this.$element
|
||||
.attr(ARIA_HIDDEN, false)
|
||||
.trigger(EVENT_SHOWN)
|
||||
.focus();
|
||||
},
|
||||
|
||||
hide: function(noTransition) {
|
||||
var $this = this.$element,
|
||||
hideEvent;
|
||||
|
||||
if (this.transitioning || !$this.hasClass(CLASS_IN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hideEvent = $.Event(EVENT_HIDE);
|
||||
$this.trigger(hideEvent);
|
||||
|
||||
if (hideEvent.isDefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$document.find('body').removeClass(CLASS_OPEN);
|
||||
this.transitioning = true;
|
||||
|
||||
if (noTransition || !this.fadable) {
|
||||
$this.removeClass(CLASS_IN);
|
||||
this.hidden();
|
||||
return;
|
||||
}
|
||||
|
||||
$this.one(EVENT_TRANSITION_END, $.proxy(this.hidden, this));
|
||||
$this.removeClass(CLASS_IN);
|
||||
},
|
||||
|
||||
hidden: function() {
|
||||
this.transitioning = false;
|
||||
this.unbind();
|
||||
this.$element
|
||||
.removeClass(CLASS_SHOWN)
|
||||
.attr(ARIA_HIDDEN, true)
|
||||
.trigger(EVENT_HIDDEN);
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
if (this.$element.hasClass(CLASS_IN)) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.show();
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorModal.DEFAULTS = {
|
||||
backdrop: false,
|
||||
keyboard: true,
|
||||
show: true
|
||||
};
|
||||
|
||||
QorModal.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorModal(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.qorModal = QorModal.plugin;
|
||||
|
||||
$(function() {
|
||||
var selector = '.qor-modal';
|
||||
|
||||
$(document)
|
||||
.on(EVENT_CLICK, '[data-toggle="qor.modal"]', function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data();
|
||||
var $target = $(data.target || $this.attr('href'));
|
||||
|
||||
QorModal.plugin.call($target, $target.data(NAMESPACE) ? 'toggle' : data);
|
||||
})
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorModal.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorModal.plugin.call($(selector, e.target));
|
||||
});
|
||||
});
|
||||
|
||||
return QorModal;
|
||||
});
|
||||
109
app/views/qor/assets/javascripts/qor/qor-radio-tabs.js
Normal file
109
app/views/qor/assets/javascripts/qor/qor-radio-tabs.js
Normal file
@@ -0,0 +1,109 @@
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var NAMESPACE = 'qor.tabbar.radio';
|
||||
var EVENT_ENABLE = 'enable.' + NAMESPACE;
|
||||
var EVENT_DISABLE = 'disable.' + NAMESPACE;
|
||||
var EVENT_CLICK = 'click.' + NAMESPACE;
|
||||
var EVENT_SWITCHED = 'switched.' + NAMESPACE;
|
||||
var CLASS_TAB = '[data-tab-target]';
|
||||
var CLASS_TAB_SOURCE = '[data-tab-source]';
|
||||
var CLASS_ACTIVE = 'is-active';
|
||||
|
||||
function QorTabRadio(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorTabRadio.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorTabRadio.prototype = {
|
||||
constructor: QorTabRadio,
|
||||
|
||||
init: function () {
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function () {
|
||||
this.$element.on(EVENT_CLICK, CLASS_TAB, this.switchTab.bind(this));
|
||||
},
|
||||
|
||||
unbind: function () {
|
||||
this.$element.off(EVENT_CLICK, CLASS_TAB, this.switchTab);
|
||||
},
|
||||
|
||||
|
||||
switchTab: function (e) {
|
||||
var $target = $(e.target),
|
||||
$element = this.$element,
|
||||
$tabs = $element.find(CLASS_TAB),
|
||||
$tabSources = $element.find(CLASS_TAB_SOURCE),
|
||||
data = $target.data(),
|
||||
tabTarget = data.tabTarget;
|
||||
|
||||
if ($target.hasClass(CLASS_ACTIVE)){
|
||||
return;
|
||||
}
|
||||
|
||||
$tabs.removeClass(CLASS_ACTIVE);
|
||||
$target.addClass(CLASS_ACTIVE);
|
||||
|
||||
$tabSources.hide().filter('[data-tab-source="' + tabTarget + '"]').show();
|
||||
$element.trigger(EVENT_SWITCHED, [$element, tabTarget]);
|
||||
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.unbind();
|
||||
}
|
||||
};
|
||||
|
||||
QorTabRadio.DEFAULTS = {};
|
||||
|
||||
QorTabRadio.plugin = function (options) {
|
||||
return this.each(function () {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorTabRadio(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction(fn = data[options])) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function () {
|
||||
var selector = '[data-toggle="qor.tab.radio"]';
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function (e) {
|
||||
QorTabRadio.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function (e) {
|
||||
QorTabRadio.plugin.call($(selector, e.target));
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorTabRadio;
|
||||
|
||||
});
|
||||
452
app/views/qor/assets/javascripts/qor/qor-redactor.js
Normal file
452
app/views/qor/assets/javascripts/qor/qor-redactor.js
Normal file
@@ -0,0 +1,452 @@
|
||||
(function(factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(["jquery"], factory);
|
||||
} else if (typeof exports === "object") {
|
||||
// Node / CommonJS
|
||||
factory(require("jquery"));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
"use strict";
|
||||
|
||||
let NAMESPACE = "qor.redactor",
|
||||
EVENT_ENABLE = "enable." + NAMESPACE,
|
||||
EVENT_DISABLE = "disable." + NAMESPACE,
|
||||
EVENT_CLICK = "click." + NAMESPACE,
|
||||
EVENT_ADD_CROP = "addCrop." + NAMESPACE,
|
||||
EVENT_REMOVE_CROP = "removeCrop." + NAMESPACE,
|
||||
EVENT_SHOWN = "shown.qor.modal",
|
||||
EVENT_HIDDEN = "hidden.qor.modal",
|
||||
EVENT_SCROLL = "scroll." + NAMESPACE,
|
||||
CLASS_WRAPPER = ".qor-cropper__wrapper",
|
||||
CLASS_SAVE = ".qor-cropper__save",
|
||||
CLASS_CROPPER_TOGGLE = ".qor-cropper__toggle--redactor";
|
||||
|
||||
function encodeCropData(data) {
|
||||
var nums = [];
|
||||
|
||||
if ($.isPlainObject(data)) {
|
||||
$.each(data, function() {
|
||||
nums.push(arguments[1]);
|
||||
});
|
||||
}
|
||||
|
||||
return nums.join();
|
||||
}
|
||||
|
||||
function decodeCropData(data) {
|
||||
var nums = data && data.split(",");
|
||||
|
||||
data = null;
|
||||
|
||||
if (nums && nums.length === 4) {
|
||||
data = {
|
||||
x: Number(nums[0]),
|
||||
y: Number(nums[1]),
|
||||
width: Number(nums[2]),
|
||||
height: Number(nums[3])
|
||||
};
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function capitalize(str) {
|
||||
if (typeof str === "string") {
|
||||
str = str.charAt(0).toUpperCase() + str.substr(1);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function getCapitalizeKeyObject(obj) {
|
||||
var newObj = {},
|
||||
key;
|
||||
|
||||
if ($.isPlainObject(obj)) {
|
||||
for (key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
newObj[capitalize(key)] = obj[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newObj;
|
||||
}
|
||||
|
||||
function replaceText(str, data) {
|
||||
if (typeof str === "string") {
|
||||
if (typeof data === "object") {
|
||||
$.each(data, function(key, val) {
|
||||
str = str.replace("$[" + String(key).toLowerCase() + "]", val);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function redactorToolbarSrcoll($toolbar, $container, toolbarFixedTopOffset) {
|
||||
let offsetTop = $container.offset().top,
|
||||
containerHeight = $container.outerHeight(),
|
||||
normallCSS = {
|
||||
position: "relative",
|
||||
top: "auto",
|
||||
width: "auto"
|
||||
},
|
||||
fixedCSS = {
|
||||
position: "fixed",
|
||||
top: toolbarFixedTopOffset,
|
||||
width: $container.width(),
|
||||
boxShadow: "none"
|
||||
};
|
||||
if (offsetTop < toolbarFixedTopOffset) {
|
||||
if (
|
||||
Math.abs(offsetTop) < Math.abs(containerHeight - toolbarFixedTopOffset)
|
||||
) {
|
||||
$toolbar.css(fixedCSS);
|
||||
$container.css("padding-top", $toolbar.outerHeight());
|
||||
} else {
|
||||
$toolbar.css(normallCSS);
|
||||
$container.css("padding-top", 0);
|
||||
}
|
||||
} else {
|
||||
$toolbar.css(normallCSS);
|
||||
$container.css("padding-top", 0);
|
||||
}
|
||||
}
|
||||
|
||||
function QorRedactor(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend(
|
||||
true,
|
||||
{},
|
||||
QorRedactor.DEFAULTS,
|
||||
$.isPlainObject(options) && options
|
||||
);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorRedactor.prototype = {
|
||||
constructor: QorRedactor,
|
||||
|
||||
init: function() {
|
||||
var options = this.options;
|
||||
var $this = this.$element;
|
||||
var $parent = $this.closest(options.parent);
|
||||
|
||||
if (!$parent.length) {
|
||||
$parent = $this.parent();
|
||||
}
|
||||
|
||||
this.$parent = $parent;
|
||||
this.$button = $(QorRedactor.BUTTON);
|
||||
this.$modal = $(replaceText(QorRedactor.MODAL, options.text)).appendTo(
|
||||
"body"
|
||||
);
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$element
|
||||
.on(EVENT_ADD_CROP, $.proxy(this.addButton, this))
|
||||
.on(EVENT_REMOVE_CROP, $.proxy(this.removeButton, this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$element
|
||||
.off(EVENT_ADD_CROP)
|
||||
.off(EVENT_REMOVE_CROP)
|
||||
.off(EVENT_SCROLL);
|
||||
},
|
||||
|
||||
addButton: function(e, image) {
|
||||
var $image = $(image);
|
||||
|
||||
this.$button
|
||||
.css("left", $(image).width() / 2)
|
||||
.prependTo($image.parent())
|
||||
.find(CLASS_CROPPER_TOGGLE)
|
||||
.one(EVENT_CLICK, $.proxy(this.crop, this, $image));
|
||||
},
|
||||
|
||||
removeButton: function() {
|
||||
this.$button.find(CLASS_CROPPER_TOGGLE).off(EVENT_CLICK);
|
||||
this.$button.detach();
|
||||
},
|
||||
|
||||
crop: function($image) {
|
||||
let options = this.options,
|
||||
url = $image.attr("src"),
|
||||
originalUrl = url,
|
||||
$clone,
|
||||
$modal = this.$modal;
|
||||
|
||||
if ($.isFunction(options.replace)) {
|
||||
originalUrl = options.replace(originalUrl);
|
||||
}
|
||||
|
||||
$clone = $(`<img src='${originalUrl}'>`);
|
||||
|
||||
$modal
|
||||
.one(EVENT_SHOWN, function() {
|
||||
$clone.cropper({
|
||||
data: decodeCropData($image.attr("data-crop-options")),
|
||||
background: false,
|
||||
movable: false,
|
||||
zoomable: false,
|
||||
scalable: false,
|
||||
rotatable: false,
|
||||
checkImageOrigin: false,
|
||||
|
||||
ready: function() {
|
||||
$modal.find(CLASS_SAVE).one(EVENT_CLICK, function() {
|
||||
var cropData = $clone.cropper("getData", true);
|
||||
|
||||
$.ajax(options.remote, {
|
||||
type: "POST",
|
||||
contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
Url: url,
|
||||
CropOptions: {
|
||||
original: getCapitalizeKeyObject(cropData)
|
||||
},
|
||||
Crop: true
|
||||
}),
|
||||
dataType: "json",
|
||||
|
||||
success: function(response) {
|
||||
if ($.isPlainObject(response) && response.url) {
|
||||
$image
|
||||
.attr("src", response.url)
|
||||
.attr("data-crop-options", encodeCropData(cropData))
|
||||
.removeAttr("style")
|
||||
.removeAttr("rel");
|
||||
|
||||
if ($.isFunction(options.complete)) {
|
||||
options.complete();
|
||||
}
|
||||
$modal.qorModal("hide");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
.one(EVENT_HIDDEN, function() {
|
||||
$clone.cropper("destroy").remove();
|
||||
})
|
||||
.qorModal("show")
|
||||
.find(CLASS_WRAPPER)
|
||||
.append($clone);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.$modal.qorModal("hide").remove();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorRedactor.DEFAULTS = {
|
||||
remote: false,
|
||||
parent: false,
|
||||
toggle: false,
|
||||
replace: null,
|
||||
complete: null,
|
||||
text: {
|
||||
title: "Crop the image",
|
||||
ok: "OK",
|
||||
cancel: "Cancel"
|
||||
}
|
||||
};
|
||||
|
||||
QorRedactor.BUTTON = `<div class="qor-redactor__image--buttons">
|
||||
<span class="qor-redactor__image--edit" contenteditable="false">Edit</span>
|
||||
<span class="qor-cropper__toggle--redactor" contenteditable="false">Crop</span>
|
||||
</div>`;
|
||||
|
||||
QorRedactor.MODAL = `<div class="qor-modal fade" tabindex="-1" role="dialog" aria-hidden="true">
|
||||
<div class="mdl-card mdl-shadow--2dp" role="document">
|
||||
<div class="mdl-card__title">
|
||||
<h2 class="mdl-card__title-text">$[title]</h2>
|
||||
</div>
|
||||
<div class="mdl-card__supporting-text">
|
||||
<div class="qor-cropper__wrapper"></div>
|
||||
</div>
|
||||
<div class="mdl-card__actions mdl-card--border">
|
||||
<a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect qor-cropper__save">$[ok]</a>
|
||||
<a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect" data-dismiss="modal">$[cancel]</a>
|
||||
</div>
|
||||
<div class="mdl-card__menu">
|
||||
<button class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect" data-dismiss="modal" aria-label="close">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
QorRedactor.plugin = function(option) {
|
||||
return this.each(function() {
|
||||
let $this = $(this),
|
||||
data = $this.data(NAMESPACE),
|
||||
config,
|
||||
fn;
|
||||
|
||||
if (!data) {
|
||||
if (!window.$R) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (/destroy/.test(option)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = {}));
|
||||
|
||||
let editorButtons = [
|
||||
"html",
|
||||
"format",
|
||||
"bold",
|
||||
"italic",
|
||||
"deleted",
|
||||
"lists",
|
||||
"image",
|
||||
"file",
|
||||
"link"
|
||||
];
|
||||
|
||||
config = {
|
||||
imageUpload: $this.data("uploadUrl"),
|
||||
fileUpload: $this.data("uploadUrl"),
|
||||
buttons: editorButtons,
|
||||
linkNewTab: true,
|
||||
linkTitle: false,
|
||||
autoparsePaste: false,
|
||||
autoparseLinks: false,
|
||||
multipleUpload: false,
|
||||
toolbarFixedTarget:
|
||||
!$this.closest(".qor-slideout").length &&
|
||||
!$this.closest(".qor-bottomsheets").length
|
||||
? $("main.mdl-layout__content").length
|
||||
? "main.mdl-layout__content"
|
||||
: document
|
||||
: document,
|
||||
|
||||
callbacks: {
|
||||
started: function() {
|
||||
let $container = $(this.container.$container.nodes[0]),
|
||||
$toolbar = $(this.toolbar.$toolbar.nodes[0]),
|
||||
isInSlideout = $(".qor-slideout").is(":visible"),
|
||||
toolbarFixedTarget,
|
||||
toolbarFixedTopOffset = 64;
|
||||
|
||||
if (isInSlideout) {
|
||||
if ($this.closest(".qor-bottomsheets").length != 0) {
|
||||
toolbarFixedTarget = $this.closest(".qor-page__body");
|
||||
toolbarFixedTopOffset = $this
|
||||
.closest(".qor-page__body")
|
||||
.offset().top;
|
||||
} else {
|
||||
toolbarFixedTarget = ".qor-slideout__body";
|
||||
toolbarFixedTopOffset = $(".qor-slideout__header").height();
|
||||
}
|
||||
} else {
|
||||
toolbarFixedTarget = ".qor-layout main.qor-page";
|
||||
toolbarFixedTopOffset =
|
||||
toolbarFixedTopOffset +
|
||||
$(toolbarFixedTarget)
|
||||
.find(".qor-page__header")
|
||||
.height();
|
||||
}
|
||||
|
||||
$(toolbarFixedTarget).on(EVENT_SCROLL, function() {
|
||||
redactorToolbarSrcoll(
|
||||
$toolbar,
|
||||
$container,
|
||||
toolbarFixedTopOffset
|
||||
);
|
||||
});
|
||||
|
||||
if (!$this.data("cropUrl")) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(
|
||||
NAMESPACE,
|
||||
(data = new QorRedactor($this, {
|
||||
remote: $this.data("cropUrl"),
|
||||
text: $this.data("text"),
|
||||
parent: ".qor-field",
|
||||
toggle: ".qor-cropper__toggle--redactor",
|
||||
replace: function(url) {
|
||||
return url.replace(/\.\w+$/, function(extension) {
|
||||
return ".original" + extension;
|
||||
});
|
||||
},
|
||||
complete: $.proxy(function() {
|
||||
this.code.sync();
|
||||
}, this)
|
||||
}))
|
||||
);
|
||||
},
|
||||
|
||||
imageUpload: function(image, json) {
|
||||
var $image = $(image);
|
||||
json.filelink && $image.prop("src", json.filelink);
|
||||
},
|
||||
|
||||
insertedLink: function(link) {
|
||||
var $link = $(link),
|
||||
description = this.link.description;
|
||||
|
||||
$link.prop("title", description ? description : $link.text());
|
||||
this.link.description = "";
|
||||
this.link.linkUrlText = "";
|
||||
this.link.insertedTriggered = true;
|
||||
},
|
||||
|
||||
fileUpload: function(link, json) {
|
||||
$(link)
|
||||
.prop("href", json.filelink)
|
||||
.html(json.filename);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.extend(config, $this.data("redactorSettings"));
|
||||
window.$R.prototype.constructor.services.editor.prototype.focus = function() {
|
||||
return false;
|
||||
};
|
||||
window.$R(this, config);
|
||||
} else {
|
||||
if (/destroy/.test(option)) {
|
||||
window.$R(this, "destroy");
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof option === "string" && $.isFunction((fn = data[option]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var selector = 'textarea[data-toggle="qor.redactor"]';
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorRedactor.plugin.call($(selector, e.target), "destroy");
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorRedactor.plugin.call($(selector, e.target));
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorRedactor;
|
||||
});
|
||||
417
app/views/qor/assets/javascripts/qor/qor-replicator.js
Normal file
417
app/views/qor/assets/javascripts/qor/qor-replicator.js
Normal file
@@ -0,0 +1,417 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
let _ = window._,
|
||||
QOR = window.QOR,
|
||||
NAMESPACE = 'qor.replicator',
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_SUBMIT = 'submit.' + NAMESPACE,
|
||||
EVENT_CLICK = 'click.' + NAMESPACE,
|
||||
EVENT_SLIDEOUTBEFORESEND = 'slideoutBeforeSend.qor.slideout.replicator',
|
||||
EVENT_SELECTCOREBEFORESEND = 'selectcoreBeforeSend.qor.selectcore.replicator bottomsheetBeforeSend.qor.bottomsheets.replicator',
|
||||
EVENT_REPLICATOR_ADDED = 'added.' + NAMESPACE,
|
||||
EVENT_REPLICATORS_ADDED = 'addedMultiple.' + NAMESPACE,
|
||||
EVENT_REPLICATORS_ADDED_DONE = 'addedMultipleDone.' + NAMESPACE,
|
||||
CLASS_CONTAINER = '.qor-fieldset-container';
|
||||
|
||||
function QorReplicator(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorReplicator.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.index = 0;
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorReplicator.prototype = {
|
||||
constructor: QorReplicator,
|
||||
|
||||
init: function() {
|
||||
let $element = this.$element,
|
||||
$template = $element.find('> .qor-field__block > .qor-fieldset--new'),
|
||||
fieldsetName;
|
||||
|
||||
this.singlePage = !($element.closest('.qor-slideout').length && $element.closest('.qor-bottomsheets').length);
|
||||
this.maxitems = $element.data('maxItem');
|
||||
this.isSortable = $element.hasClass('qor-fieldset-sortable');
|
||||
|
||||
if (!$template.length || $element.closest('.qor-fieldset--new').length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Should destroy all components here
|
||||
$template.trigger('disable');
|
||||
// remove data-select2-id attribute or select2 will disable all previous instance
|
||||
$template.find('select[data-toggle]').removeAttr('data-select2-id');
|
||||
|
||||
// if have isMultiple data value or template length large than 1
|
||||
this.isMultipleTemplate = $element.data('isMultiple');
|
||||
|
||||
if (this.isMultipleTemplate) {
|
||||
this.fieldsetName = [];
|
||||
this.template = {};
|
||||
this.index = [];
|
||||
|
||||
$template.each((i, ele) => {
|
||||
fieldsetName = $(ele).data('fieldsetName');
|
||||
if (fieldsetName) {
|
||||
this.template[fieldsetName] = $(ele).prop('outerHTML');
|
||||
this.fieldsetName.push(fieldsetName);
|
||||
}
|
||||
});
|
||||
|
||||
this.parseMultiple();
|
||||
} else {
|
||||
this.parse($template.prop('outerHTML'));
|
||||
}
|
||||
|
||||
$template.hide();
|
||||
this.bind();
|
||||
this.resetButton();
|
||||
this.resetPositionButton();
|
||||
},
|
||||
|
||||
resetPositionButton: function() {
|
||||
let sortableButton = this.$element.find('> .qor-sortable__button');
|
||||
|
||||
if (this.isSortable) {
|
||||
if (this.getCurrentItems() > 1) {
|
||||
sortableButton.show();
|
||||
} else {
|
||||
sortableButton.hide();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getCurrentItems: function() {
|
||||
return this.$element.find('> .qor-field__block > .qor-fieldset').not('.qor-fieldset--new,.is-deleted').length;
|
||||
},
|
||||
|
||||
toggleButton: function(isHide) {
|
||||
let $button = this.$element.find('> .qor-field__block > .qor-fieldset__add');
|
||||
|
||||
if (isHide) {
|
||||
$button.hide();
|
||||
} else {
|
||||
$button.show();
|
||||
}
|
||||
},
|
||||
|
||||
resetButton: function() {
|
||||
if (this.maxitems <= this.getCurrentItems()) {
|
||||
this.toggleButton(true);
|
||||
} else {
|
||||
this.toggleButton();
|
||||
}
|
||||
},
|
||||
|
||||
parse: function($tmp) {
|
||||
let template;
|
||||
|
||||
if (!$tmp) {
|
||||
return;
|
||||
}
|
||||
template = this.initTemplate($tmp);
|
||||
|
||||
this.template = template.template;
|
||||
this.index = template.index;
|
||||
},
|
||||
|
||||
parseMultiple: function() {
|
||||
let template,
|
||||
name,
|
||||
fieldsetName = this.fieldsetName;
|
||||
|
||||
for (let i = 0, len = fieldsetName.length; i < len; i++) {
|
||||
name = fieldsetName[i];
|
||||
template = this.initTemplate(this.template[name]);
|
||||
this.template[name] = template.template;
|
||||
this.index.push(template.index);
|
||||
}
|
||||
|
||||
this.multipleIndex = _.max(this.index);
|
||||
},
|
||||
|
||||
initTemplate: function(template) {
|
||||
let i,
|
||||
deepLevel = this.$element.parents(CLASS_CONTAINER).length;
|
||||
|
||||
template = template.replace(/(\w+)\="(\S*\[\d+\]\S*)"/g, function(attribute, name, value) {
|
||||
value = value.replace(/^(\S*)\[(\d+)\]([^\[\]]*)$/, function(input, prefix, index) {
|
||||
if (input === value) {
|
||||
if (name === 'name' && !i) {
|
||||
i = index;
|
||||
}
|
||||
|
||||
if (deepLevel) {
|
||||
// assume input = QorResource.SerializableMeta.Menus[1].SubMenus[2].Items[3].URL
|
||||
// if deepLevel = 1, input should be QorResource.SerializableMeta.Menus[1].SubMenus[{{index}}].Items[3].URL
|
||||
// if deepLevel = 2, input should be QorResource.SerializableMeta.Menus[1].SubMenus[2].Items[{{index}}].URL
|
||||
|
||||
let newInput = '',
|
||||
splitStr = input.split(/\[\d+\]/), // ["QorResource.SerializableMeta.Menus", ".SubMenus", ".Items", ".URL"]
|
||||
sortNumbers = input.match(/\[\d+\]/g); // ["[1]", "[2]", "[3]"]
|
||||
|
||||
for (let j = 0; j < splitStr.length; j++) {
|
||||
let str = '';
|
||||
if (j === deepLevel) {
|
||||
str = '[{{index}}]';
|
||||
} else if (j < sortNumbers.length) {
|
||||
str = sortNumbers[j];
|
||||
}
|
||||
newInput += splitStr[j] + str;
|
||||
}
|
||||
|
||||
return newInput;
|
||||
} else {
|
||||
return input.replace(/\[\d+\]/, '[{{index}}]');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return name + '="' + value + '"';
|
||||
});
|
||||
|
||||
return {
|
||||
template: template,
|
||||
index: parseFloat(i) + 5 //make sure the index is different from original.
|
||||
};
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
let options = this.options;
|
||||
|
||||
this.$element.on(EVENT_CLICK, options.addClass, $.proxy(this.add, this)).on(EVENT_CLICK, options.delClass, $.proxy(this.del, this));
|
||||
|
||||
this.singlePage && $(document).on(EVENT_SUBMIT, '.mdl-layout__container form', this.clearFieldData);
|
||||
$(document)
|
||||
.on(EVENT_SLIDEOUTBEFORESEND, '.qor-slideout', this.clearFieldDataInSlideout)
|
||||
.on(EVENT_SELECTCOREBEFORESEND, this.clearFieldDataInBottomsheet);
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$element.off(EVENT_CLICK);
|
||||
|
||||
this.singlePage && $(document).off(EVENT_SUBMIT, '.mdl-layout__container form', this.clearFieldData);
|
||||
$(document)
|
||||
.off(EVENT_SLIDEOUTBEFORESEND, '.qor-slideout', this.clearFieldDataInSlideout)
|
||||
.off(EVENT_SELECTCOREBEFORESEND, this.clearFieldDataInBottomsheet);
|
||||
},
|
||||
|
||||
clearFieldData: function() {
|
||||
$('.qor-fieldset--new').remove();
|
||||
},
|
||||
|
||||
clearFieldDataInSlideout: function() {
|
||||
$('.qor-slideout .qor-fieldset--new').remove();
|
||||
},
|
||||
|
||||
clearFieldDataInBottomsheet: function() {
|
||||
$('.qor-bottomsheets .qor-fieldset--new').remove();
|
||||
},
|
||||
|
||||
add: function(e, data, isAutomatically) {
|
||||
let options = this.options,
|
||||
$item,
|
||||
template,
|
||||
$target = $(e.target).closest(options.addClass);
|
||||
|
||||
if (this.maxitems <= this.getCurrentItems()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isMultipleTemplate) {
|
||||
let templateName = $target.data('template'),
|
||||
parents = $target.closest(this.$element),
|
||||
parentsChildren = parents.children(options.childrenClass),
|
||||
$fieldset = $target.closest(options.childrenClass).children('fieldset');
|
||||
|
||||
template = this.template[templateName];
|
||||
$item = $(template.replace(/\{\{index\}\}/g, this.multipleIndex));
|
||||
|
||||
// get input kind from add button then add into QorResource.Rules[1].Kind input
|
||||
for (let dataKey in $target.data()) {
|
||||
if (dataKey.match(/^sync/)) {
|
||||
let k = dataKey.replace(/^sync/, '');
|
||||
$item.find("input[name*='." + k + "']").val($target.data(dataKey));
|
||||
}
|
||||
}
|
||||
|
||||
if ($fieldset.length) {
|
||||
$fieldset.last().after($item.show());
|
||||
} else {
|
||||
parentsChildren.prepend($item.show());
|
||||
}
|
||||
$item.data('itemIndex', this.multipleIndex).removeClass('qor-fieldset--new');
|
||||
this.multipleIndex++;
|
||||
} else {
|
||||
if (!isAutomatically) {
|
||||
$item = this.addSingle();
|
||||
$target.before($item.show());
|
||||
this.index++;
|
||||
} else {
|
||||
if (data && data.length) {
|
||||
this.addMultiple(data);
|
||||
$(document).trigger(EVENT_REPLICATORS_ADDED_DONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAutomatically) {
|
||||
$item.trigger('enable');
|
||||
$(document).trigger(EVENT_REPLICATOR_ADDED, [$item]);
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
this.resetPositionButton();
|
||||
this.resetButton();
|
||||
},
|
||||
|
||||
addMultiple: function(data) {
|
||||
let $item;
|
||||
|
||||
for (let i = 0, len = data.length; i < len; i++) {
|
||||
$item = this.addSingle();
|
||||
this.index++;
|
||||
$(document).trigger(EVENT_REPLICATORS_ADDED, [$item, data[i]]);
|
||||
}
|
||||
},
|
||||
|
||||
addSingle: function() {
|
||||
let $item,
|
||||
$element = this.$element;
|
||||
|
||||
if (!this.template) {
|
||||
return;
|
||||
}
|
||||
|
||||
$item = $(this.template.replace(/\{\{index\}\}/g, this.index));
|
||||
// add order property for sortable fieldset
|
||||
if (this.isSortable) {
|
||||
let order = $element.find('> .qor-field__block > .qor-sortable__item').not('.qor-fieldset--new').length;
|
||||
$item
|
||||
.attr('order-index', order)
|
||||
.attr('order-item', `item_${order}`)
|
||||
.css('order', order);
|
||||
}
|
||||
|
||||
$item.data('itemIndex', this.index).removeClass('qor-fieldset--new');
|
||||
|
||||
return $item;
|
||||
},
|
||||
|
||||
del: function(e) {
|
||||
let options = this.options,
|
||||
$item = $(e.target).closest(options.itemClass),
|
||||
$alert,
|
||||
that = this,
|
||||
message = {
|
||||
confirm:
|
||||
$(e.target)
|
||||
.closest(options.delClass)
|
||||
.data('confirm') || 'Are you sure?'
|
||||
};
|
||||
|
||||
QOR.qorConfirm(message, function(confirm) {
|
||||
if (confirm) {
|
||||
$item
|
||||
.addClass('is-deleted')
|
||||
.children(':visible')
|
||||
.addClass('hidden')
|
||||
.hide();
|
||||
$alert = $(options.alertTemplate.replace('{{name}}', that.parseName($item)));
|
||||
$alert.find(options.undoClass).one(
|
||||
EVENT_CLICK,
|
||||
function() {
|
||||
if (that.maxitems <= that.getCurrentItems()) {
|
||||
window.QOR.qorConfirm(that.$element.data('maxItemHint'));
|
||||
return false;
|
||||
}
|
||||
|
||||
$item.find('> .qor-fieldset__alert').remove();
|
||||
$item
|
||||
.removeClass('is-deleted')
|
||||
.children('.hidden')
|
||||
.removeClass('hidden')
|
||||
.show();
|
||||
that.resetButton();
|
||||
that.resetPositionButton();
|
||||
}.bind(this)
|
||||
);
|
||||
that.resetButton();
|
||||
that.resetPositionButton();
|
||||
$item.append($alert);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
parseName: function($item) {
|
||||
let name = $item.find('input[name]').attr('name') || $item.find('textarea[name]').attr('name');
|
||||
|
||||
if (name) {
|
||||
return name.replace(/[^\[\]]+$/, '');
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorReplicator.DEFAULTS = {
|
||||
itemClass: '.qor-fieldset',
|
||||
newClass: '.qor-fieldset--new',
|
||||
addClass: '.qor-fieldset__add',
|
||||
delClass: '.qor-fieldset__delete',
|
||||
childrenClass: '.qor-field__block',
|
||||
undoClass: '.qor-fieldset__undo',
|
||||
alertTemplate:
|
||||
'<div class="qor-fieldset__alert">' +
|
||||
'<input type="hidden" name="{{name}}._destroy" value="1">' +
|
||||
'<button class="mdl-button mdl-button--accent mdl-js-button mdl-js-ripple-effect qor-fieldset__undo" type="button">Undo delete</button>' +
|
||||
'</div>'
|
||||
};
|
||||
|
||||
QorReplicator.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
let $this = $(this),
|
||||
data = $this.data(NAMESPACE),
|
||||
fn;
|
||||
|
||||
if (!data) {
|
||||
$this.data(NAMESPACE, (data = new QorReplicator(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
|
||||
fn.call(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
let selector = CLASS_CONTAINER;
|
||||
let options = {};
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorReplicator.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorReplicator.plugin.call($(selector, e.target), options);
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorReplicator;
|
||||
});
|
||||
163
app/views/qor/assets/javascripts/qor/qor-search.js
Normal file
163
app/views/qor/assets/javascripts/qor/qor-search.js
Normal file
@@ -0,0 +1,163 @@
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function ($) {
|
||||
|
||||
'use strict';
|
||||
var location = window.location;
|
||||
var componentHandler = window.componentHandler;
|
||||
var history = window.history;
|
||||
var NAMESPACE = 'qor.globalSearch';
|
||||
var EVENT_ENABLE = 'enable.' + NAMESPACE;
|
||||
var EVENT_DISABLE = 'disable.' + NAMESPACE;
|
||||
var EVENT_CLICK = 'click.' + NAMESPACE;
|
||||
|
||||
var SEARCH_RESOURCE = '.qor-global-search--resource';
|
||||
var SEARCH_RESULTS = '.qor-global-search--results';
|
||||
var QOR_TABLE = '.qor-table';
|
||||
var IS_ACTIVE = 'is-active';
|
||||
|
||||
function QorSearchCenter(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorSearchCenter.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorSearchCenter.prototype = {
|
||||
constructor: QorSearchCenter,
|
||||
|
||||
init: function () {
|
||||
this.bind();
|
||||
this.initTab();
|
||||
},
|
||||
|
||||
bind: function () {
|
||||
this.$element.on(EVENT_CLICK, $.proxy(this.click, this));
|
||||
},
|
||||
|
||||
unbind: function () {
|
||||
this.$element.off(EVENT_CLICK, this.check);
|
||||
},
|
||||
|
||||
initTab: function () {
|
||||
var locationSearch = location.search;
|
||||
var resourceName;
|
||||
if (/resource_name/.test(locationSearch)){
|
||||
resourceName = locationSearch.match(/resource_name=\w+/g).toString().split('=')[1];
|
||||
$(SEARCH_RESOURCE).removeClass(IS_ACTIVE);
|
||||
$('[data-resource="' + resourceName + '"]').addClass(IS_ACTIVE);
|
||||
}
|
||||
},
|
||||
|
||||
click : function (e) {
|
||||
var $target = $(e.target);
|
||||
var data = $target.data();
|
||||
|
||||
if ($target.is(SEARCH_RESOURCE)){
|
||||
var oldUrl = location.href.replace(/#/g, '');
|
||||
var newUrl;
|
||||
var newResourceName = data.resource;
|
||||
var hasResource = /resource_name/.test(oldUrl);
|
||||
var hasKeyword = /keyword/.test(oldUrl);
|
||||
var resourceParam = 'resource_name=' + newResourceName;
|
||||
var searchSymbol = hasKeyword ? '&' : '?keyword=&';
|
||||
|
||||
if (newResourceName){
|
||||
if (hasResource){
|
||||
newUrl = oldUrl.replace(/resource_name=\w+/g, resourceParam);
|
||||
} else {
|
||||
newUrl = oldUrl + searchSymbol + resourceParam;
|
||||
}
|
||||
} else {
|
||||
newUrl = oldUrl.replace(/&resource_name=\w+/g, '');
|
||||
}
|
||||
|
||||
if (history.pushState){
|
||||
this.fetchSearch(newUrl, $target);
|
||||
} else {
|
||||
location.href = newUrl;
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
fetchSearch: function (url,$target) {
|
||||
var title = document.title;
|
||||
|
||||
$.ajax(url, {
|
||||
method: 'GET',
|
||||
dataType: 'html',
|
||||
beforeSend: function () {
|
||||
$('.mdl-spinner').remove();
|
||||
$(SEARCH_RESULTS).prepend('<div class="mdl-spinner mdl-js-spinner is-active"></div>').find('.qor-section').hide();
|
||||
componentHandler.upgradeElement(document.querySelector('.mdl-spinner'));
|
||||
},
|
||||
success: function (html) {
|
||||
var result = $(html).find(SEARCH_RESULTS).html();
|
||||
$(SEARCH_RESOURCE).removeClass(IS_ACTIVE);
|
||||
$target.addClass(IS_ACTIVE);
|
||||
// change location URL without refresh page
|
||||
history.pushState({ Page: url, Title: title }, title, url);
|
||||
$('.mdl-spinner').remove();
|
||||
$(SEARCH_RESULTS).removeClass('loading').html(result);
|
||||
componentHandler.upgradeElements(document.querySelectorAll(QOR_TABLE));
|
||||
},
|
||||
error: function (xhr, textStatus, errorThrown) {
|
||||
$(SEARCH_RESULTS).find('.qor-section').show();
|
||||
$('.mdl-spinner').remove();
|
||||
window.alert([textStatus, errorThrown].join(': '));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.unbind();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
QorSearchCenter.DEFAULTS = {
|
||||
};
|
||||
|
||||
QorSearchCenter.plugin = function (options) {
|
||||
return this.each(function () {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
$this.data(NAMESPACE, (data = new QorSearchCenter(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction(fn = data[options])) {
|
||||
fn.call(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function () {
|
||||
var selector = '[data-toggle="qor.global.search"]';
|
||||
var options = {};
|
||||
|
||||
$(document).
|
||||
on(EVENT_DISABLE, function (e) {
|
||||
QorSearchCenter.plugin.call($(selector, e.target), 'destroy');
|
||||
}).
|
||||
on(EVENT_ENABLE, function (e) {
|
||||
QorSearchCenter.plugin.call($(selector, e.target), options);
|
||||
}).
|
||||
triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorSearchCenter;
|
||||
|
||||
});
|
||||
170
app/views/qor/assets/javascripts/qor/qor-select-core.js
Normal file
170
app/views/qor/assets/javascripts/qor/qor-select-core.js
Normal file
@@ -0,0 +1,170 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
let FormData = window.FormData,
|
||||
QOR = window.QOR,
|
||||
NAMESPACE = 'qor.selectcore',
|
||||
EVENT_SELECTCORE_BEFORESEND = 'selectcoreBeforeSend.' + NAMESPACE,
|
||||
EVENT_ONSELECT = 'afterSelected.' + NAMESPACE,
|
||||
EVENT_ONSUBMIT = 'afterSubmitted.' + NAMESPACE,
|
||||
EVENT_CLICK = 'click.' + NAMESPACE,
|
||||
EVENT_SUBMIT = 'submit.' + NAMESPACE,
|
||||
CLASS_TABLE = 'table.qor-js-table tr',
|
||||
CLASS_FORM = 'form';
|
||||
|
||||
function QorSelectCore(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorSelectCore.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorSelectCore.prototype = {
|
||||
constructor: QorSelectCore,
|
||||
|
||||
init: function() {
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$element.on(EVENT_CLICK, CLASS_TABLE, this.processingData.bind(this)).on(EVENT_SUBMIT, CLASS_FORM, this.submit.bind(this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$element.off(EVENT_CLICK, CLASS_TABLE).off(EVENT_SUBMIT, CLASS_FORM);
|
||||
},
|
||||
|
||||
processingData: function(e) {
|
||||
let $this = $(e.target).closest('tr'),
|
||||
$bottomsheets = $this.closest('.qor-bottomsheets'),
|
||||
data = {},
|
||||
url,
|
||||
options = this.options,
|
||||
onSelect = options.onSelect,
|
||||
loading = options.loading;
|
||||
|
||||
data = $.extend({}, data, $this.data());
|
||||
data.$clickElement = $this;
|
||||
|
||||
url = data.mediaLibraryUrl || data.url;
|
||||
|
||||
if (loading && $.isFunction(loading)) {
|
||||
loading($bottomsheets);
|
||||
}
|
||||
|
||||
if (url) {
|
||||
|
||||
$.getJSON(url, function(json) {
|
||||
json.MediaOption && (json.MediaOption = JSON.parse(json.MediaOption));
|
||||
data = $.extend({}, json, data);
|
||||
if (onSelect && $.isFunction(onSelect)) {
|
||||
onSelect(data, e);
|
||||
$(document).trigger(EVENT_ONSELECT);
|
||||
}
|
||||
}).always(function() {
|
||||
$bottomsheets.find('.qor-media-loading').remove();
|
||||
});
|
||||
|
||||
} else {
|
||||
if (onSelect && $.isFunction(onSelect)) {
|
||||
onSelect(data, e);
|
||||
$(document).trigger(EVENT_ONSELECT);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
submit: function(e) {
|
||||
let form = e.target,
|
||||
$form = $(form),
|
||||
_this = this,
|
||||
$submit = $form.find(':submit'),
|
||||
data,
|
||||
$loading = $(QOR.$formLoading),
|
||||
onSubmit = this.options.onSubmit;
|
||||
|
||||
$(document).trigger(EVENT_SELECTCORE_BEFORESEND);
|
||||
|
||||
$form.find('.qor-fieldset--new').remove();
|
||||
|
||||
if (FormData) {
|
||||
e.preventDefault();
|
||||
|
||||
$.ajax($form.prop('action'), {
|
||||
method: $form.prop('method'),
|
||||
data: new FormData(form),
|
||||
dataType: 'json',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
beforeSend: function() {
|
||||
$('.qor-submit-loading').remove();
|
||||
$loading.appendTo($submit.prop('disabled', true).closest('.qor-form__actions')).trigger('enable.qor.material');
|
||||
},
|
||||
success: function(json) {
|
||||
json.MediaOption && (json.MediaOption = JSON.parse(json.MediaOption));
|
||||
data = json;
|
||||
data.primaryKey = data.ID;
|
||||
|
||||
$('.qor-error').remove();
|
||||
|
||||
if (onSubmit && $.isFunction(onSubmit)) {
|
||||
onSubmit(data, e);
|
||||
$(document).trigger(EVENT_ONSUBMIT);
|
||||
} else {
|
||||
_this.refresh();
|
||||
}
|
||||
},
|
||||
error: function(err) {
|
||||
QOR.handleAjaxError(err);
|
||||
},
|
||||
complete: function() {
|
||||
$submit.prop('disabled', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
setTimeout(function() {
|
||||
window.location.reload();
|
||||
}, 350);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
}
|
||||
};
|
||||
|
||||
QorSelectCore.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
let $this = $(this),
|
||||
data = $this.data(NAMESPACE),
|
||||
fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
$this.data(NAMESPACE, (data = new QorSelectCore(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.qorSelectCore = QorSelectCore.plugin;
|
||||
|
||||
return QorSelectCore;
|
||||
});
|
||||
339
app/views/qor/assets/javascripts/qor/qor-select-many.js
Normal file
339
app/views/qor/assets/javascripts/qor/qor-select-many.js
Normal file
@@ -0,0 +1,339 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
let $body = $('body'),
|
||||
$document = $(document),
|
||||
Mustache = window.Mustache,
|
||||
NAMESPACE = 'qor.selectone',
|
||||
PARENT_NAMESPACE = 'qor.bottomsheets',
|
||||
EVENT_CLICK = 'click.' + NAMESPACE,
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_RELOAD = 'reload.' + PARENT_NAMESPACE,
|
||||
CLASS_CLEAR_SELECT = '.qor-selected-many__remove',
|
||||
CLASS_UNDO_DELETE = '.qor-selected-many__undo',
|
||||
CLASS_DELETED_ITEM = 'qor-selected-many__deleted',
|
||||
CLASS_SELECT_FIELD = '.qor-field__selected-many',
|
||||
CLASS_SELECT_INPUT = '.qor-field__selectmany-input',
|
||||
CLASS_SELECT_ICON = '.qor-select__select-icon',
|
||||
CLASS_SELECT_HINT = '.qor-selectmany__hint',
|
||||
CLASS_SELECT_ALL = '.qor-selectmany__selectall',
|
||||
CLASS_PARENT = '.qor-field__selectmany',
|
||||
CLASS_SELECTED = 'is_selected',
|
||||
CLASS_MANY = 'qor-bottomsheets__select-many';
|
||||
|
||||
function QorSelectMany(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorSelectMany.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorSelectMany.prototype = {
|
||||
constructor: QorSelectMany,
|
||||
|
||||
init: function() {
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
$document.on(EVENT_RELOAD, `.${CLASS_MANY}`, this.reloadData.bind(this));
|
||||
|
||||
this.$element
|
||||
.on(EVENT_CLICK, CLASS_CLEAR_SELECT, this.clearSelect.bind(this))
|
||||
.on(EVENT_CLICK, '[data-select-modal="many"]', this.openBottomSheets.bind(this))
|
||||
.on(EVENT_CLICK, CLASS_UNDO_DELETE, this.undoDelete.bind(this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
$document.off(EVENT_CLICK, '[data-select-modal="many"]').off(EVENT_RELOAD, `.${CLASS_MANY}`);
|
||||
this.$element.off(EVENT_CLICK, CLASS_CLEAR_SELECT).off(EVENT_CLICK, CLASS_UNDO_DELETE);
|
||||
},
|
||||
|
||||
clearSelect: function(e) {
|
||||
var $target = $(e.target),
|
||||
$selectFeild = $target.closest(CLASS_PARENT);
|
||||
|
||||
$target.closest('[data-primary-key]').addClass(CLASS_DELETED_ITEM);
|
||||
this.updateSelectInputData($selectFeild);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
undoDelete: function(e) {
|
||||
var $target = $(e.target),
|
||||
$selectFeild = $target.closest(CLASS_PARENT);
|
||||
|
||||
$target.closest('[data-primary-key]').removeClass(CLASS_DELETED_ITEM);
|
||||
this.updateSelectInputData($selectFeild);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
openBottomSheets: function(e) {
|
||||
let $this = $(e.target),
|
||||
data = $this.data();
|
||||
|
||||
this.BottomSheets = $body.data('qor.bottomsheets');
|
||||
this.bottomsheetsData = data;
|
||||
|
||||
this.$selector = data.selectId ? $(data.selectId) : $this.closest(CLASS_PARENT).find('select');
|
||||
this.$selectFeild = this.$selector.closest(CLASS_PARENT).find(CLASS_SELECT_FIELD);
|
||||
|
||||
// select many templates
|
||||
this.SELECT_MANY_SELECTED_ICON = $('[name="select-many-selected-icon"]').html();
|
||||
this.SELECT_MANY_UNSELECTED_ICON = $('[name="select-many-unselected-icon"]').html();
|
||||
this.SELECT_MANY_HINT = $('[name="select-many-hint"]').html();
|
||||
this.SELECT_MANY_TEMPLATE = $('[name="select-many-template"]').html();
|
||||
|
||||
data.url = data.selectListingUrl;
|
||||
|
||||
if (data.selectDefaultCreating) {
|
||||
data.url = data.selectCreatingUrl;
|
||||
}
|
||||
|
||||
this.BottomSheets.open(data, this.handleSelectMany.bind(this));
|
||||
},
|
||||
|
||||
reloadData: function() {
|
||||
this.initItems();
|
||||
},
|
||||
|
||||
renderSelectMany: function(data) {
|
||||
return Mustache.render(this.SELECT_MANY_TEMPLATE, data);
|
||||
},
|
||||
|
||||
renderHint: function(data) {
|
||||
return Mustache.render(this.SELECT_MANY_HINT, data);
|
||||
},
|
||||
|
||||
initItems: function() {
|
||||
var $tr = this.$bottomsheets.find('tbody tr'),
|
||||
selectedIconTmpl = this.SELECT_MANY_SELECTED_ICON,
|
||||
unSelectedIconTmpl = this.SELECT_MANY_UNSELECTED_ICON,
|
||||
selectedIDs = [],
|
||||
primaryKey,
|
||||
$selectedItems = this.$selectFeild.find('[data-primary-key]').not('.' + CLASS_DELETED_ITEM);
|
||||
|
||||
$selectedItems.each(function() {
|
||||
selectedIDs.push($(this).data().primaryKey);
|
||||
});
|
||||
|
||||
$tr.each(function() {
|
||||
var $this = $(this),
|
||||
$td = $this.find('td:first');
|
||||
|
||||
primaryKey = $this.data().primaryKey;
|
||||
|
||||
if (selectedIDs.indexOf(primaryKey) != '-1') {
|
||||
$this.addClass(CLASS_SELECTED);
|
||||
$td.append(selectedIconTmpl);
|
||||
} else {
|
||||
$td.append(unSelectedIconTmpl);
|
||||
}
|
||||
});
|
||||
|
||||
this.updateHint(this.getSelectedItemData());
|
||||
},
|
||||
|
||||
getSelectedItemData: function() {
|
||||
var selecedItems = this.$selectFeild.find('[data-primary-key]').not('.' + CLASS_DELETED_ITEM);
|
||||
return {
|
||||
selectedNum: selecedItems.length
|
||||
};
|
||||
},
|
||||
|
||||
updateHint: function(data) {
|
||||
var template;
|
||||
|
||||
$.extend(data, this.bottomsheetsData);
|
||||
template = this.renderHint(data);
|
||||
|
||||
this.$bottomsheets.find(CLASS_SELECT_HINT).remove();
|
||||
this.$bottomsheets.find('.qor-page__body').before(template);
|
||||
},
|
||||
|
||||
updateSelectInputData: function($selectFeild) {
|
||||
var $selectList = $selectFeild ? $selectFeild : this.$selectFeild,
|
||||
$selectedItems = $selectList.find('[data-primary-key]').not('.' + CLASS_DELETED_ITEM),
|
||||
$selector = $selectFeild ? $selectFeild.find(CLASS_SELECT_INPUT) : this.$selector,
|
||||
$options = $selector.find('option'),
|
||||
$option,
|
||||
data,
|
||||
primaryKey;
|
||||
|
||||
$options.prop('selected', false);
|
||||
|
||||
$selectedItems.each(function() {
|
||||
primaryKey = $(this).data().primaryKey;
|
||||
$option = $options.filter('[value="' + primaryKey + '"]');
|
||||
|
||||
if (!$option.length) {
|
||||
data = {
|
||||
primaryKey: primaryKey,
|
||||
displayName: ''
|
||||
};
|
||||
$option = $(Mustache.render(QorSelectMany.SELECT_MANY_OPTION_TEMPLATE, data));
|
||||
$selector.append($option);
|
||||
}
|
||||
|
||||
$option.prop('selected', true);
|
||||
});
|
||||
},
|
||||
|
||||
changeIcon: function($ele, template) {
|
||||
$ele.find(CLASS_SELECT_ICON).remove();
|
||||
$ele.find('td:first').prepend(template);
|
||||
},
|
||||
|
||||
removeItem: function(data) {
|
||||
var primaryKey = data.primaryKey;
|
||||
|
||||
this.$selectFeild
|
||||
.find('[data-primary-key="' + primaryKey + '"]')
|
||||
.find(CLASS_CLEAR_SELECT)
|
||||
.click();
|
||||
this.changeIcon(data.$clickElement, this.SELECT_MANY_UNSELECTED_ICON);
|
||||
},
|
||||
|
||||
addItem: function(data, isNewData) {
|
||||
var template = this.renderSelectMany(data),
|
||||
$option,
|
||||
$list = this.$selectFeild.find('[data-primary-key="' + data.primaryKey + '"]');
|
||||
|
||||
if ($list.length) {
|
||||
if ($list.hasClass(CLASS_DELETED_ITEM)) {
|
||||
$list.removeClass(CLASS_DELETED_ITEM);
|
||||
this.updateSelectInputData();
|
||||
this.changeIcon(data.$clickElement, this.SELECT_MANY_SELECTED_ICON);
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.$selectFeild.append(template);
|
||||
|
||||
if (isNewData) {
|
||||
$option = $(Mustache.render(QorSelectMany.SELECT_MANY_OPTION_TEMPLATE, data));
|
||||
$option.appendTo(this.$selector);
|
||||
$option.prop('selected', true);
|
||||
this.$bottomsheets.remove();
|
||||
if (!$('.qor-bottomsheets').is(':visible')) {
|
||||
$('body').removeClass('qor-bottomsheets-open');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.changeIcon(data.$clickElement, this.SELECT_MANY_SELECTED_ICON);
|
||||
},
|
||||
|
||||
handleSelectMany: function($bottomsheets) {
|
||||
let options = {
|
||||
onSelect: this.onSelectResults.bind(this), // render selected item after click item lists
|
||||
onSubmit: this.onSubmitResults.bind(this) // render new items after new item form submitted
|
||||
};
|
||||
|
||||
$bottomsheets.qorSelectCore(options).addClass(CLASS_MANY);
|
||||
$bottomsheets.on(EVENT_CLICK, CLASS_SELECT_ALL, this.handleSelectAll.bind(this));
|
||||
this.$bottomsheets = $bottomsheets;
|
||||
this.initItems();
|
||||
},
|
||||
|
||||
handleSelectAll: function() {
|
||||
let $trs = this.$bottomsheets.find('.qor-table tbody tr'),
|
||||
$trsHasUnselected = $trs.not('.is_selected');
|
||||
|
||||
if ($trsHasUnselected.length) {
|
||||
$trsHasUnselected.click();
|
||||
} else {
|
||||
$trs.click();
|
||||
}
|
||||
},
|
||||
|
||||
onSelectResults: function(data) {
|
||||
this.handleResults(data);
|
||||
},
|
||||
|
||||
onSubmitResults: function(data) {
|
||||
this.handleResults(data, true);
|
||||
},
|
||||
|
||||
handleResults: function(data, isNewData) {
|
||||
data.displayName = data.Text || data.Name || data.Title || data.Code || data[Object.keys(data)[0]];
|
||||
|
||||
data.displayName = (data.displayName).escapeSymbol();
|
||||
|
||||
if (isNewData) {
|
||||
this.addItem(data, true);
|
||||
return;
|
||||
}
|
||||
|
||||
var $element = data.$clickElement,
|
||||
isSelected;
|
||||
|
||||
$element.toggleClass(CLASS_SELECTED);
|
||||
isSelected = $element.hasClass(CLASS_SELECTED);
|
||||
|
||||
if (isSelected) {
|
||||
this.addItem(data);
|
||||
} else {
|
||||
this.removeItem(data);
|
||||
}
|
||||
|
||||
this.updateHint(this.getSelectedItemData());
|
||||
this.updateSelectInputData();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorSelectMany.SELECT_MANY_OPTION_TEMPLATE = '<option value="[[ primaryKey ]]" >[[ displayName ]]</option>';
|
||||
|
||||
QorSelectMany.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorSelectMany(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var selector = '[data-toggle="qor.selectmany"]';
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorSelectMany.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorSelectMany.plugin.call($(selector, e.target));
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorSelectMany;
|
||||
});
|
||||
214
app/views/qor/assets/javascripts/qor/qor-select-one.js
Normal file
214
app/views/qor/assets/javascripts/qor/qor-select-one.js
Normal file
@@ -0,0 +1,214 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
let $body = $('body'),
|
||||
$document = $(document),
|
||||
Mustache = window.Mustache,
|
||||
NAMESPACE = 'qor.selectone',
|
||||
PARENT_NAMESPACE = 'qor.bottomsheets',
|
||||
EVENT_CLICK = 'click.' + NAMESPACE,
|
||||
EVENT_ENABLE = 'enable.' + NAMESPACE,
|
||||
EVENT_DISABLE = 'disable.' + NAMESPACE,
|
||||
EVENT_RELOAD = 'reload.' + PARENT_NAMESPACE,
|
||||
CLASS_CLEAR_SELECT = '.qor-selected__remove',
|
||||
CLASS_CHANGE_SELECT = '.qor-selected__change',
|
||||
CLASS_SELECT_FIELD = '.qor-field__selected',
|
||||
CLASS_SELECT_INPUT = '.qor-field__selectone-input',
|
||||
CLASS_SELECT_TRIGGER = '.qor-field__selectone-trigger',
|
||||
CLASS_PARENT = '.qor-field__selectone',
|
||||
CLASS_SELECTED = 'is_selected',
|
||||
CLASS_ONE = 'qor-bottomsheets__select-one';
|
||||
|
||||
function QorSelectOne(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorSelectOne.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorSelectOne.prototype = {
|
||||
constructor: QorSelectOne,
|
||||
|
||||
init: function() {
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
$document.on(EVENT_RELOAD, `.${CLASS_ONE}`, this.reloadData.bind(this));
|
||||
this.$element
|
||||
.on(EVENT_CLICK, CLASS_CLEAR_SELECT, this.clearSelect.bind(this))
|
||||
.on(EVENT_CLICK, '[data-selectone-url]', this.openBottomSheets.bind(this))
|
||||
.on(EVENT_CLICK, CLASS_CHANGE_SELECT, this.changeSelect);
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
$document.off(EVENT_CLICK, '[data-selectone-url]').off(EVENT_RELOAD, `.${CLASS_ONE}`);
|
||||
this.$element.off(EVENT_CLICK, CLASS_CLEAR_SELECT).off(EVENT_CLICK, CLASS_CHANGE_SELECT);
|
||||
},
|
||||
|
||||
clearSelect: function(e) {
|
||||
var $target = $(e.target),
|
||||
$parent = $target.closest(CLASS_PARENT);
|
||||
|
||||
$parent.find(CLASS_SELECT_FIELD).remove();
|
||||
$parent.find(CLASS_SELECT_INPUT).html('');
|
||||
$parent.find(CLASS_SELECT_INPUT)[0].value = '';
|
||||
$parent.find(CLASS_SELECT_TRIGGER).show();
|
||||
|
||||
$parent.trigger('qor.selectone.unselected');
|
||||
return false;
|
||||
},
|
||||
|
||||
changeSelect: function() {
|
||||
var $target = $(this),
|
||||
$parent = $target.closest(CLASS_PARENT);
|
||||
|
||||
$parent.find(CLASS_SELECT_TRIGGER).trigger('click');
|
||||
},
|
||||
|
||||
openBottomSheets: function(e) {
|
||||
var $this = $(e.target),
|
||||
data = $this.data();
|
||||
|
||||
this.BottomSheets = $body.data('qor.bottomsheets');
|
||||
this.$parent = $this.closest(CLASS_PARENT);
|
||||
|
||||
data.url = data.selectoneUrl;
|
||||
|
||||
this.SELECT_ONE_SELECTED_ICON = $('[name="select-one-selected-icon"]').html();
|
||||
this.BottomSheets.open(data, this.handleSelectOne.bind(this));
|
||||
},
|
||||
|
||||
initItem: function() {
|
||||
var $selectFeild = this.$parent.find(CLASS_SELECT_FIELD),
|
||||
selectedID;
|
||||
|
||||
if (!$selectFeild.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
selectedID = $selectFeild.data().primaryKey;
|
||||
|
||||
if (selectedID) {
|
||||
this.$bottomsheets
|
||||
.find('tr[data-primary-key="' + selectedID + '"]')
|
||||
.addClass(CLASS_SELECTED)
|
||||
.find('td:first')
|
||||
.append(this.SELECT_ONE_SELECTED_ICON);
|
||||
}
|
||||
},
|
||||
|
||||
reloadData: function() {
|
||||
this.initItem();
|
||||
},
|
||||
|
||||
renderSelectOne: function(data) {
|
||||
return Mustache.render($('[name="select-one-selected-template"]').html(), data);
|
||||
},
|
||||
|
||||
handleSelectOne: function($bottomsheets) {
|
||||
var options = {
|
||||
onSelect: this.onSelectResults.bind(this), //render selected item after click item lists
|
||||
onSubmit: this.onSubmitResults.bind(this) //render new items after new item form submitted
|
||||
};
|
||||
|
||||
$bottomsheets.qorSelectCore(options).addClass(CLASS_ONE);
|
||||
this.$bottomsheets = $bottomsheets;
|
||||
this.initItem();
|
||||
},
|
||||
|
||||
onSelectResults: function(data) {
|
||||
this.handleResults(data);
|
||||
},
|
||||
|
||||
onSubmitResults: function(data) {
|
||||
this.handleResults(data, true);
|
||||
},
|
||||
|
||||
handleResults: function(data) {
|
||||
var template,
|
||||
$parent = this.$parent,
|
||||
$select = $parent.find('select'),
|
||||
$selectFeild = $parent.find(CLASS_SELECT_FIELD);
|
||||
|
||||
data.displayName = data.Text || data.Name || data.Title || data.Code || data[Object.keys(data)[0]];
|
||||
data.selectoneValue = data.primaryKey || data.ID;
|
||||
|
||||
data.displayName = (data.displayName).escapeSymbol();
|
||||
|
||||
if (!$select.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
template = this.renderSelectOne(data);
|
||||
|
||||
if ($selectFeild.length) {
|
||||
$selectFeild.remove();
|
||||
}
|
||||
|
||||
$parent.prepend(template);
|
||||
$parent.find(CLASS_SELECT_TRIGGER).hide();
|
||||
|
||||
$select.html(Mustache.render(QorSelectOne.SELECT_ONE_OPTION_TEMPLATE, data));
|
||||
$select[0].value = data.primaryKey || data.ID;
|
||||
|
||||
$parent.trigger('qor.selectone.selected', [data]);
|
||||
|
||||
this.$bottomsheets.qorSelectCore('destroy').remove();
|
||||
if (!$('.qor-bottomsheets').is(':visible')) {
|
||||
$('body').removeClass('qor-bottomsheets-open');
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorSelectOne.SELECT_ONE_OPTION_TEMPLATE = '<option value="[[ selectoneValue ]]" selected>[[ displayName ]]</option>';
|
||||
|
||||
QorSelectOne.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorSelectOne(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction((fn = data[options]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var selector = '[data-toggle="qor.selectone"]';
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorSelectOne.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorSelectOne.plugin.call($(selector, e.target));
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorSelectOne;
|
||||
});
|
||||
274
app/views/qor/assets/javascripts/qor/qor-selector.js
Normal file
274
app/views/qor/assets/javascripts/qor/qor-selector.js
Normal file
@@ -0,0 +1,274 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
var $document = $(document);
|
||||
var NAMESPACE = 'qor.selector';
|
||||
var EVENT_ENABLE = 'enable.' + NAMESPACE;
|
||||
var EVENT_DISABLE = 'disable.' + NAMESPACE;
|
||||
var EVENT_CLICK = 'click.' + NAMESPACE;
|
||||
var EVENT_SELECTOR_CHANGE = 'selectorChanged.' + NAMESPACE;
|
||||
var CLASS_OPEN = 'open';
|
||||
var CLASS_ACTIVE = 'active';
|
||||
var CLASS_HOVER = 'hover';
|
||||
var CLASS_SELECTED = 'selected';
|
||||
var CLASS_DISABLED = 'disabled';
|
||||
var CLASS_CLEARABLE = 'clearable';
|
||||
var SELECTOR_SELECTED = '.' + CLASS_SELECTED;
|
||||
var SELECTOR_TOGGLE = '.qor-selector-toggle';
|
||||
var SELECTOR_LABEL = '.qor-selector-label';
|
||||
var SELECTOR_CLEAR = '.qor-selector-clear';
|
||||
var SELECTOR_MENU = '.qor-selector-menu';
|
||||
var CLASS_BOTTOMSHEETS = '.qor-bottomsheets';
|
||||
|
||||
function QorSelector(element, options) {
|
||||
this.options = options;
|
||||
this.$element = $(element);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorSelector.prototype = {
|
||||
constructor: QorSelector,
|
||||
|
||||
init: function() {
|
||||
var $this = this.$element;
|
||||
|
||||
this.placeholder = $this.attr('placeholder') || $this.attr('name') || 'Select';
|
||||
this.build();
|
||||
},
|
||||
|
||||
build: function() {
|
||||
var $this = this.$element;
|
||||
var $selector = $(QorSelector.TEMPLATE);
|
||||
var alignedClass = this.options.aligned + '-aligned';
|
||||
var data = {};
|
||||
var eleData = $this.data();
|
||||
var hover = eleData.hover;
|
||||
var paramName = $this.attr('name');
|
||||
|
||||
this.isBottom = eleData.position == 'bottom';
|
||||
|
||||
hover && $selector.addClass(CLASS_HOVER);
|
||||
|
||||
$selector
|
||||
.addClass(alignedClass)
|
||||
.find(SELECTOR_MENU)
|
||||
.html(function() {
|
||||
var list = [];
|
||||
|
||||
$this.children().each(function() {
|
||||
var $this = $(this);
|
||||
var selected = $this.attr('selected');
|
||||
var disabled = $this.attr('disabled');
|
||||
var value = $this.attr('value');
|
||||
var label = $this.text();
|
||||
var classNames = [];
|
||||
|
||||
if (selected) {
|
||||
classNames.push(CLASS_SELECTED);
|
||||
data.value = value;
|
||||
data.label = label;
|
||||
data.paramName = paramName;
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
classNames.push(CLASS_DISABLED);
|
||||
}
|
||||
|
||||
list.push(
|
||||
'<li' +
|
||||
(classNames.length ? ' class="' + classNames.join(' ') + '"' : '') +
|
||||
' data-value="' +
|
||||
value +
|
||||
'"' +
|
||||
' data-label="' +
|
||||
label +
|
||||
'"' +
|
||||
' data-param-name="' +
|
||||
paramName +
|
||||
'"' +
|
||||
'>' +
|
||||
label +
|
||||
'</li>'
|
||||
);
|
||||
});
|
||||
|
||||
return list.join('');
|
||||
});
|
||||
|
||||
this.$selector = $selector;
|
||||
$this.hide().after($selector);
|
||||
$selector.find(SELECTOR_TOGGLE).data('paramName', paramName);
|
||||
this.pick(data, true);
|
||||
this.bind();
|
||||
},
|
||||
|
||||
unbuild: function() {
|
||||
this.unbind();
|
||||
this.$selector.remove();
|
||||
this.$element.show();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$selector.on(EVENT_CLICK, $.proxy(this.click, this));
|
||||
$document.on(EVENT_CLICK, $.proxy(this.close, this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$selector.off(EVENT_CLICK, this.click);
|
||||
},
|
||||
|
||||
click: function(e) {
|
||||
var $target = $(e.target);
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
if ($target.is(SELECTOR_CLEAR)) {
|
||||
this.clear();
|
||||
} else if ($target.is('li')) {
|
||||
if (!$target.hasClass(CLASS_SELECTED) && !$target.hasClass(CLASS_DISABLED)) {
|
||||
this.pick($target.data());
|
||||
}
|
||||
|
||||
this.close();
|
||||
} else if ($target.closest(SELECTOR_TOGGLE).length) {
|
||||
this.open();
|
||||
}
|
||||
},
|
||||
|
||||
pick: function(data, initialized) {
|
||||
var $selector = this.$selector;
|
||||
var selected = !!data.value;
|
||||
var $element = this.$element;
|
||||
|
||||
$selector
|
||||
.find(SELECTOR_TOGGLE)
|
||||
.toggleClass(CLASS_ACTIVE, selected)
|
||||
.toggleClass(CLASS_CLEARABLE, selected && this.options.clearable)
|
||||
.find(SELECTOR_LABEL)
|
||||
.text(data.label || this.placeholder);
|
||||
|
||||
if (!initialized) {
|
||||
$selector
|
||||
.find(SELECTOR_MENU)
|
||||
.children('[data-value="' + data.value + '"]')
|
||||
.addClass(CLASS_SELECTED)
|
||||
.siblings(SELECTOR_SELECTED)
|
||||
.removeClass(CLASS_SELECTED);
|
||||
|
||||
$element.val(data.value);
|
||||
|
||||
if ($element.closest(CLASS_BOTTOMSHEETS).length && !$element.closest('[data-toggle="qor.filter"]').length) {
|
||||
// If action is in bottom sheet, will trigger filterChanged.qor.selector event, add passed data.value parameter to event.
|
||||
$(CLASS_BOTTOMSHEETS).trigger(EVENT_SELECTOR_CHANGE, [data.value, data.paramName]);
|
||||
} else {
|
||||
$element.trigger('change');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
clear: function() {
|
||||
var $element = this.$element;
|
||||
|
||||
this.$selector
|
||||
.find(SELECTOR_TOGGLE)
|
||||
.removeClass(CLASS_ACTIVE)
|
||||
.removeClass(CLASS_CLEARABLE)
|
||||
.find(SELECTOR_LABEL)
|
||||
.text(this.placeholder)
|
||||
.end()
|
||||
.end()
|
||||
.find(SELECTOR_MENU)
|
||||
.children(SELECTOR_SELECTED)
|
||||
.removeClass(CLASS_SELECTED);
|
||||
|
||||
$element.val('').trigger('change');
|
||||
},
|
||||
|
||||
open: function() {
|
||||
// Close other opened dropdowns first
|
||||
$document.triggerHandler(EVENT_CLICK);
|
||||
$('.qor-filter__dropdown').hide();
|
||||
|
||||
// Open the current dropdown
|
||||
this.$selector.addClass(CLASS_OPEN);
|
||||
if (this.isBottom) {
|
||||
this.$selector.addClass('bottom');
|
||||
}
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.$selector.removeClass(CLASS_OPEN);
|
||||
if (this.isBottom) {
|
||||
this.$selector.removeClass('bottom');
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbuild();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorSelector.DEFAULTS = {
|
||||
aligned: 'left',
|
||||
clearable: false
|
||||
};
|
||||
|
||||
QorSelector.TEMPLATE =
|
||||
'<div class="qor-selector">' +
|
||||
'<a class="qor-selector-toggle">' +
|
||||
'<span class="qor-selector-label"></span>' +
|
||||
'<i class="material-icons qor-selector-arrow">arrow_drop_down</i>' +
|
||||
'<i class="material-icons qor-selector-clear">clear</i>' +
|
||||
'</a>' +
|
||||
'<ul class="qor-selector-menu"></ul>' +
|
||||
'</div>';
|
||||
|
||||
QorSelector.plugin = function(option) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var options;
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(option)) {
|
||||
return;
|
||||
}
|
||||
|
||||
options = $.extend({}, QorSelector.DEFAULTS, $this.data(), typeof option === 'object' && option);
|
||||
$this.data(NAMESPACE, (data = new QorSelector(this, options)));
|
||||
}
|
||||
|
||||
if (typeof option === 'string' && $.isFunction((fn = data[option]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var selector = '[data-toggle="qor.selector"]';
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorSelector.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorSelector.plugin.call($(selector, e.target));
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorSelector;
|
||||
});
|
||||
689
app/views/qor/assets/javascripts/qor/qor-slideout.js
Normal file
689
app/views/qor/assets/javascripts/qor/qor-slideout.js
Normal file
@@ -0,0 +1,689 @@
|
||||
(function(factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(["jquery"], factory);
|
||||
} else if (typeof exports === "object") {
|
||||
// Node / CommonJS
|
||||
factory(require("jquery"));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
"use strict";
|
||||
|
||||
let $document = $(document),
|
||||
FormData = window.FormData,
|
||||
QOR_Translations = window.QOR_Translations,
|
||||
_ = window._,
|
||||
QOR = window.QOR,
|
||||
NAMESPACE = "qor.slideout",
|
||||
EVENT_KEYUP = "keyup." + NAMESPACE,
|
||||
EVENT_CLICK = "click." + NAMESPACE,
|
||||
EVENT_SUBMIT = "submit." + NAMESPACE,
|
||||
EVENT_SHOW = "show." + NAMESPACE,
|
||||
EVENT_SLIDEOUT_SUBMIT_COMPLEMENT = "slideoutSubmitComplete." + NAMESPACE,
|
||||
EVENT_SLIDEOUT_CLOSED = "slideoutClosed." + NAMESPACE,
|
||||
EVENT_SLIDEOUT_LOADED = "slideoutLoaded." + NAMESPACE,
|
||||
EVENT_SLIDEOUT_BEFORESEND = "slideoutBeforeSend." + NAMESPACE,
|
||||
EVENT_SHOWN = "shown." + NAMESPACE,
|
||||
EVENT_HIDE = "hide." + NAMESPACE,
|
||||
EVENT_HIDDEN = "hidden." + NAMESPACE,
|
||||
EVENT_TRANSITIONEND = "transitionend",
|
||||
CLASS_OPEN = "qor-slideout-open",
|
||||
CLASS_MINI = "qor-slideout-mini",
|
||||
CLASS_IS_SHOWN = "is-shown",
|
||||
CLASS_IS_SLIDED = "is-slided",
|
||||
CLASS_IS_SELECTED = "is-selected",
|
||||
CLASS_MAIN_CONTENT = ".mdl-layout__content.qor-page",
|
||||
CLASS_HEADER_LOCALE = ".qor-actions__locale",
|
||||
CLASS_BODY_LOADING = ".qor-body__loading";
|
||||
|
||||
function replaceHtml(el, html) {
|
||||
let oldEl = typeof el === "string" ? document.getElementById(el) : el,
|
||||
newEl = oldEl.cloneNode(false);
|
||||
newEl.innerHTML = html;
|
||||
oldEl.parentNode.replaceChild(newEl, oldEl);
|
||||
return newEl;
|
||||
}
|
||||
|
||||
function pushArrary($ele, isScript) {
|
||||
let array = [],
|
||||
prop = "href";
|
||||
|
||||
isScript && (prop = "src");
|
||||
$ele.each(function() {
|
||||
array.push($(this).attr(prop));
|
||||
});
|
||||
return _.uniq(array);
|
||||
}
|
||||
|
||||
function execSlideoutEvents(url, response) {
|
||||
// exec qorSliderAfterShow after script loaded
|
||||
var qorSliderAfterShow = $.fn.qorSliderAfterShow;
|
||||
for (var name in qorSliderAfterShow) {
|
||||
if (
|
||||
qorSliderAfterShow.hasOwnProperty(name) &&
|
||||
!qorSliderAfterShow[name]["isLoaded"]
|
||||
) {
|
||||
qorSliderAfterShow[name]["isLoaded"] = true;
|
||||
qorSliderAfterShow[name].call(this, url, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadScripts(srcs, data, callback) {
|
||||
let scriptsLoaded = 0;
|
||||
|
||||
for (let i = 0, len = srcs.length; i < len; i++) {
|
||||
let script = document.createElement("script");
|
||||
|
||||
script.onload = function() {
|
||||
scriptsLoaded++;
|
||||
|
||||
if (scriptsLoaded === srcs.length) {
|
||||
if ($.isFunction(callback)) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
if (data && data.url && data.response) {
|
||||
execSlideoutEvents(data.url, data.response);
|
||||
}
|
||||
};
|
||||
|
||||
script.src = srcs[i];
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
|
||||
function loadStyles(srcs) {
|
||||
let ss = document.createElement("link"),
|
||||
src = srcs.shift();
|
||||
|
||||
ss.type = "text/css";
|
||||
ss.rel = "stylesheet";
|
||||
ss.onload = function() {
|
||||
if (srcs.length) {
|
||||
loadStyles(srcs);
|
||||
}
|
||||
};
|
||||
ss.href = src;
|
||||
document.getElementsByTagName("head")[0].appendChild(ss);
|
||||
}
|
||||
|
||||
function compareScripts($scripts) {
|
||||
let $currentPageScripts = $("script"),
|
||||
slideoutScripts = pushArrary($scripts, true),
|
||||
currentPageScripts = pushArrary($currentPageScripts, true),
|
||||
scriptDiff = _.difference(slideoutScripts, currentPageScripts);
|
||||
return scriptDiff;
|
||||
}
|
||||
|
||||
function compareLinks($links) {
|
||||
let $currentStyles = $("link"),
|
||||
slideoutStyles = pushArrary($links),
|
||||
currentStyles = pushArrary($currentStyles),
|
||||
styleDiff = _.difference(slideoutStyles, currentStyles);
|
||||
|
||||
return styleDiff;
|
||||
}
|
||||
|
||||
function QorSlideout(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend(
|
||||
{},
|
||||
QorSlideout.DEFAULTS,
|
||||
$.isPlainObject(options) && options
|
||||
);
|
||||
this.slided = false;
|
||||
this.disabled = false;
|
||||
this.slideoutType = false;
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorSlideout.prototype = {
|
||||
constructor: QorSlideout,
|
||||
|
||||
init: function() {
|
||||
this.build();
|
||||
this.bind();
|
||||
},
|
||||
|
||||
build: function() {
|
||||
var $slideout;
|
||||
|
||||
this.$slideout = $slideout = $(QorSlideout.TEMPLATE).appendTo("body");
|
||||
this.$slideoutTemplate = $slideout.html();
|
||||
},
|
||||
|
||||
unbuild: function() {
|
||||
this.$slideout.remove();
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
this.$slideout
|
||||
.on(EVENT_SUBMIT, "form", this.submit.bind(this))
|
||||
.on(
|
||||
EVENT_CLICK,
|
||||
".qor-slideout__fullscreen",
|
||||
this.toggleSlideoutMode.bind(this)
|
||||
)
|
||||
.on(EVENT_CLICK, '[data-dismiss="slideout"]', this.hide.bind(this));
|
||||
|
||||
$document.on(EVENT_KEYUP, $.proxy(this.keyup, this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$slideout.off(EVENT_SUBMIT, this.submit).off(EVENT_CLICK);
|
||||
|
||||
$document.off(EVENT_KEYUP, this.keyup);
|
||||
},
|
||||
|
||||
keyup: function(e) {
|
||||
if (e.which === 27) {
|
||||
if (
|
||||
$(".qor-bottomsheets").is(":visible") ||
|
||||
$(".qor-modal").is(":visible") ||
|
||||
$("#redactor-modal-box").length ||
|
||||
$("#dialog").is(":visible")
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hide();
|
||||
this.removeSelectedClass();
|
||||
}
|
||||
},
|
||||
|
||||
loadExtraResource: function(data) {
|
||||
let styleDiff = compareLinks(data.$links),
|
||||
scriptDiff = compareScripts(data.$scripts);
|
||||
|
||||
if (styleDiff.length) {
|
||||
loadStyles(styleDiff);
|
||||
}
|
||||
|
||||
if (scriptDiff.length) {
|
||||
loadScripts(scriptDiff, data);
|
||||
}
|
||||
},
|
||||
|
||||
removeSelectedClass: function() {
|
||||
this.$element.find("[data-url]").removeClass(CLASS_IS_SELECTED);
|
||||
},
|
||||
|
||||
addLoading: function() {
|
||||
$(CLASS_BODY_LOADING).remove();
|
||||
var $loading = $(QorSlideout.TEMPLATE_LOADING);
|
||||
$loading.appendTo($("body")).trigger("enable.qor.material");
|
||||
},
|
||||
|
||||
toggleSlideoutMode: function() {
|
||||
this.$slideout
|
||||
.toggleClass("qor-slideout__fullscreen")
|
||||
.find(".qor-slideout__fullscreen i")
|
||||
.toggle();
|
||||
},
|
||||
|
||||
checkRichedutorHTMLTags: function(source){
|
||||
var DOMHolderArray = new Array();
|
||||
var tagsArray = new Array();
|
||||
var lines = source.value.split('\n');
|
||||
for (var x = 0; x < lines.length; x++) {
|
||||
tagsArray = lines[x].match(/<(\/{1})?\w+((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)>/g);
|
||||
if (tagsArray) {
|
||||
for (var i = 0; i < tagsArray.length; i++) {
|
||||
if (tagsArray[i].indexOf('</') >= 0) {
|
||||
let elementToPop = tagsArray[i].substr(2, tagsArray[i].length - 3);
|
||||
elementToPop = elementToPop.replace(/ /g, '');
|
||||
for (var j = DOMHolderArray.length - 1; j >= 0; j--) {
|
||||
if (DOMHolderArray[j].element == elementToPop) {
|
||||
DOMHolderArray.splice(j, 1);
|
||||
if (elementToPop != 'html') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var tag = new Object();
|
||||
tag.full = tagsArray[i];
|
||||
tag.line = x + 1;
|
||||
if (tag.full.indexOf(' ') > 0) {
|
||||
tag.element = tag.full.substr(1, tag.full.indexOf(' ') - 1);
|
||||
} else {
|
||||
tag.element = tag.full.substr(1, tag.full.length - 2);
|
||||
}
|
||||
var selfClosingTags = new Array('area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr');
|
||||
var isSelfClosing = false;
|
||||
for (var y = 0; y < selfClosingTags.length; y++) {
|
||||
if (selfClosingTags[y].localeCompare(tag.element) == 0) {
|
||||
isSelfClosing = true;
|
||||
}
|
||||
}
|
||||
if (isSelfClosing == false) {
|
||||
DOMHolderArray.push(tag);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DOMHolderArray.length;
|
||||
|
||||
},
|
||||
|
||||
submit: function(e) {
|
||||
let $slideout = this.$slideout,
|
||||
form = e.target,
|
||||
$form = $(form),
|
||||
_this = this,
|
||||
$loading = $(QOR.$formLoading),
|
||||
$submit = $form.find(":submit"),
|
||||
hasNotClosedTags = false;
|
||||
|
||||
if ($form.data("normal-submit")) {
|
||||
return;
|
||||
}
|
||||
|
||||
$slideout.trigger(EVENT_SLIDEOUT_BEFORESEND);
|
||||
|
||||
if (!FormData) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
|
||||
document.querySelectorAll('.qor-redactor-box .redactor-source').forEach(function(item) {
|
||||
if(_this.checkRichedutorHTMLTags(item)){
|
||||
hasNotClosedTags=true;
|
||||
}
|
||||
});
|
||||
|
||||
if(hasNotClosedTags){
|
||||
QOR.qorConfirm(QOR_Translations.slideoutCheckHTMLTagsError);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.submitXHR = $.ajax($form.prop("action"), {
|
||||
method: $form.prop("method"),
|
||||
data: new FormData(form),
|
||||
dataType: "html",
|
||||
processData: false,
|
||||
contentType: false,
|
||||
beforeSend: function() {
|
||||
$(".qor-submit-loading").remove();
|
||||
$loading
|
||||
.appendTo(
|
||||
$submit.prop("disabled", true).closest(".qor-form__actions")
|
||||
)
|
||||
.trigger("enable.qor.material");
|
||||
$.fn.qorSlideoutBeforeHide = null;
|
||||
},
|
||||
success: function() {
|
||||
let returnUrl = $form.data("returnUrl"),
|
||||
refreshUrl = $form.data("refreshUrl");
|
||||
|
||||
$slideout.trigger(EVENT_SLIDEOUT_SUBMIT_COMPLEMENT);
|
||||
|
||||
if (refreshUrl) {
|
||||
window.location.href = refreshUrl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (returnUrl == "refresh") {
|
||||
_this.refresh();
|
||||
return;
|
||||
}
|
||||
|
||||
if (returnUrl && returnUrl != "refresh") {
|
||||
_this.load(returnUrl);
|
||||
} else {
|
||||
_this.refresh();
|
||||
}
|
||||
},
|
||||
error: function(err) {
|
||||
QOR.handleAjaxError(err);
|
||||
},
|
||||
complete: function() {
|
||||
$submit.prop("disabled", false);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
load: function(url, data) {
|
||||
var options = this.options;
|
||||
var method;
|
||||
var dataType;
|
||||
var load;
|
||||
var $slideout = this.$slideout;
|
||||
var $title;
|
||||
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
data = $.isPlainObject(data) ? data : {};
|
||||
|
||||
method = data.method ? data.method : "GET";
|
||||
dataType = data.datatype ? data.datatype : "html";
|
||||
|
||||
load = $.proxy(function() {
|
||||
$.ajax(url, {
|
||||
method: method,
|
||||
dataType: dataType,
|
||||
cache: true,
|
||||
ifModified: true,
|
||||
success: $.proxy(function(response) {
|
||||
let $response,
|
||||
$content,
|
||||
$qorFormContainer,
|
||||
$scripts,
|
||||
$links,
|
||||
bodyClass;
|
||||
|
||||
$(CLASS_BODY_LOADING).remove();
|
||||
|
||||
if (method === "GET") {
|
||||
$response = $(response);
|
||||
$content = $response.find(CLASS_MAIN_CONTENT);
|
||||
$qorFormContainer = $content.find(".qor-form-container");
|
||||
this.slideoutType =
|
||||
$qorFormContainer.length &&
|
||||
$qorFormContainer.data().slideoutType;
|
||||
|
||||
if (!$content.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let bodyHtml = response.match(
|
||||
/<\s*body.*>[\s\S]*<\s*\/body\s*>/gi
|
||||
);
|
||||
if (bodyHtml) {
|
||||
bodyHtml = bodyHtml
|
||||
.join("")
|
||||
.replace(/<\s*body/gi, "<div")
|
||||
.replace(/<\s*\/body/gi, "</div");
|
||||
bodyClass = $(bodyHtml).prop("class");
|
||||
$("body").addClass(bodyClass);
|
||||
|
||||
let data = {
|
||||
$scripts: $response.filter("script"),
|
||||
$links: $response.filter("link"),
|
||||
url: url,
|
||||
response: response
|
||||
};
|
||||
|
||||
this.loadExtraResource(data);
|
||||
}
|
||||
|
||||
$content
|
||||
.find(".qor-button--cancel")
|
||||
.attr("data-dismiss", "slideout")
|
||||
.removeAttr("href");
|
||||
|
||||
$scripts = compareScripts($content.find("script[src]"));
|
||||
$links = compareLinks($content.find("link[href]"));
|
||||
|
||||
if ($scripts.length) {
|
||||
let data = {
|
||||
url: url,
|
||||
response: response
|
||||
};
|
||||
|
||||
loadScripts($scripts, data, function() {});
|
||||
}
|
||||
|
||||
if ($links.length) {
|
||||
loadStyles($links);
|
||||
}
|
||||
|
||||
$content.find("script[src],link[href]").remove();
|
||||
|
||||
// reset slideout header and body
|
||||
$slideout.html(this.$slideoutTemplate);
|
||||
$title = $slideout.find(".qor-slideout__title");
|
||||
this.$body = $slideout.find(".qor-slideout__body");
|
||||
|
||||
$title.html($response.find(options.title).html());
|
||||
replaceHtml(
|
||||
$slideout.find(".qor-slideout__body")[0],
|
||||
$content.html()
|
||||
);
|
||||
this.$body.find(CLASS_HEADER_LOCALE).remove();
|
||||
|
||||
$slideout
|
||||
.one(EVENT_SHOWN, function() {
|
||||
$(this).trigger("enable");
|
||||
})
|
||||
.one(EVENT_HIDDEN, function() {
|
||||
$(this).trigger("disable");
|
||||
});
|
||||
|
||||
$slideout.find(".qor-slideout__opennew").attr("href", url);
|
||||
this.show();
|
||||
|
||||
// callback for after slider loaded HTML
|
||||
// this callback is deprecated, use slideoutLoaded.qor.slideout event.
|
||||
var qorSliderAfterShow = $.fn.qorSliderAfterShow;
|
||||
if (qorSliderAfterShow) {
|
||||
for (var name in qorSliderAfterShow) {
|
||||
if (
|
||||
qorSliderAfterShow.hasOwnProperty(name) &&
|
||||
$.isFunction(qorSliderAfterShow[name])
|
||||
) {
|
||||
qorSliderAfterShow[name]["isLoaded"] = true;
|
||||
qorSliderAfterShow[name].call(this, url, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// will trigger slideoutLoaded.qor.slideout event after slideout loaded
|
||||
$slideout.trigger(EVENT_SLIDEOUT_LOADED, [url, response]);
|
||||
} else {
|
||||
if (data.returnUrl) {
|
||||
this.load(data.returnUrl);
|
||||
} else {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
}, this),
|
||||
|
||||
error: $.proxy(function() {
|
||||
var errors;
|
||||
$(CLASS_BODY_LOADING).remove();
|
||||
if ($(".qor-error span").length > 0) {
|
||||
errors = $(".qor-error span")
|
||||
.map(function() {
|
||||
return $(this).text();
|
||||
})
|
||||
.get()
|
||||
.join(", ");
|
||||
} else {
|
||||
errors = QOR_Translations.serverError;
|
||||
}
|
||||
window.alert(errors);
|
||||
}, this)
|
||||
});
|
||||
}, this);
|
||||
|
||||
if (this.slided) {
|
||||
this.hide(true);
|
||||
this.$slideout.one(EVENT_HIDDEN, load);
|
||||
} else {
|
||||
load();
|
||||
}
|
||||
},
|
||||
|
||||
open: function(options) {
|
||||
this.addLoading();
|
||||
this.load(options.url, options.data);
|
||||
},
|
||||
|
||||
reload: function(url) {
|
||||
this.hide();
|
||||
this.load(url);
|
||||
},
|
||||
|
||||
show: function() {
|
||||
var $slideout = this.$slideout;
|
||||
var showEvent;
|
||||
|
||||
if (this.slided) {
|
||||
return;
|
||||
}
|
||||
|
||||
showEvent = $.Event(EVENT_SHOW);
|
||||
$slideout.trigger(showEvent);
|
||||
|
||||
if (showEvent.isDefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$slideout.removeClass(CLASS_MINI);
|
||||
this.slideoutType == "mini" && $slideout.addClass(CLASS_MINI);
|
||||
|
||||
$slideout.addClass(CLASS_IS_SHOWN).get(0).offsetWidth;
|
||||
$slideout
|
||||
.one(EVENT_TRANSITIONEND, $.proxy(this.shown, this))
|
||||
.addClass(CLASS_IS_SLIDED)
|
||||
.scrollTop(0);
|
||||
},
|
||||
|
||||
shown: function() {
|
||||
this.slided = true;
|
||||
// Disable to scroll body element
|
||||
$("body").addClass(CLASS_OPEN);
|
||||
this.$slideout
|
||||
.trigger("beforeEnable.qor.slideout")
|
||||
.trigger(EVENT_SHOWN)
|
||||
.trigger("afterEnable.qor.slideout");
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
let message = {
|
||||
confirm: QOR_Translations.slideoutCloseWarning
|
||||
};
|
||||
|
||||
if ($.fn.qorSlideoutBeforeHide) {
|
||||
QOR.qorConfirm(
|
||||
message,
|
||||
function(confirm) {
|
||||
if (confirm) {
|
||||
this.hideSlideout();
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
} else {
|
||||
this.hideSlideout();
|
||||
}
|
||||
|
||||
this.removeSelectedClass();
|
||||
},
|
||||
|
||||
hideSlideout: function() {
|
||||
var $slideout = this.$slideout;
|
||||
var hideEvent;
|
||||
var $datePicker = $(".qor-datepicker").not(".hidden");
|
||||
|
||||
// remove onbeforeunload event
|
||||
window.onbeforeunload = null;
|
||||
$.fn.qorSlideoutBeforeHide = null;
|
||||
|
||||
this.submitXHR && this.submitXHR.abort();
|
||||
|
||||
if ($datePicker.length) {
|
||||
$datePicker.addClass("hidden");
|
||||
}
|
||||
|
||||
if (!this.slided) {
|
||||
return;
|
||||
}
|
||||
|
||||
hideEvent = $.Event(EVENT_HIDE);
|
||||
$slideout.trigger(hideEvent);
|
||||
|
||||
if (hideEvent.isDefaultPrevented()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$slideout
|
||||
.one(EVENT_TRANSITIONEND, $.proxy(this.hidden, this))
|
||||
.removeClass(`${CLASS_IS_SLIDED} qor-slideout__fullscreen`);
|
||||
$slideout.trigger(EVENT_SLIDEOUT_CLOSED);
|
||||
},
|
||||
|
||||
hidden: function() {
|
||||
this.slided = false;
|
||||
|
||||
// Enable to scroll body element
|
||||
$("body").removeClass(CLASS_OPEN);
|
||||
|
||||
this.$slideout.removeClass(CLASS_IS_SHOWN).trigger(EVENT_HIDDEN);
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
this.hide();
|
||||
|
||||
setTimeout(function() {
|
||||
window.location.reload();
|
||||
}, 350);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.unbuild();
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorSlideout.DEFAULTS = {
|
||||
title: ".qor-form-title, .mdl-layout-title",
|
||||
content: false
|
||||
};
|
||||
|
||||
QorSlideout.TEMPLATE = `<div class="qor-slideout">
|
||||
<div class="qor-slideout__header">
|
||||
<div class="qor-slideout__header-link">
|
||||
<a href="#" target="_blank" class="mdl-button mdl-button--icon mdl-js-button mdl-js-repple-effect qor-slideout__opennew"><i class="material-icons">open_in_new</i></a>
|
||||
<a href="#" class="mdl-button mdl-button--icon mdl-js-button mdl-js-repple-effect qor-slideout__fullscreen">
|
||||
<i class="material-icons">fullscreen</i>
|
||||
<i class="material-icons" style="display: none;">fullscreen_exit</i>
|
||||
</a>
|
||||
</div>
|
||||
<button type="button" class="mdl-button mdl-button--icon mdl-js-button mdl-js-repple-effect qor-slideout__close" data-dismiss="slideout">
|
||||
<span class="material-icons">close</span>
|
||||
</button>
|
||||
<h3 class="qor-slideout__title"></h3>
|
||||
</div>
|
||||
<div class="qor-slideout__body"></div>
|
||||
</div>`;
|
||||
|
||||
QorSlideout.TEMPLATE_LOADING = `<div class="qor-body__loading">
|
||||
<div><div class="mdl-spinner mdl-js-spinner is-active qor-layout__bottomsheet-spinner"></div></div>
|
||||
</div>`;
|
||||
|
||||
QorSlideout.plugin = function(options) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorSlideout(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === "string" && $.isFunction((fn = data[options]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.fn.qorSlideout = QorSlideout.plugin;
|
||||
|
||||
return QorSlideout;
|
||||
});
|
||||
110
app/views/qor/assets/javascripts/qor/qor-sorter.js
Normal file
110
app/views/qor/assets/javascripts/qor/qor-sorter.js
Normal file
@@ -0,0 +1,110 @@
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var location = window.location;
|
||||
var NAMESPACE = 'qor.sorter';
|
||||
var EVENT_ENABLE = 'enable.' + NAMESPACE;
|
||||
var EVENT_DISABLE = 'disable.' + NAMESPACE;
|
||||
var EVENT_CLICK = 'click.' + NAMESPACE;
|
||||
var CLASS_IS_SORTABLE = 'is-sortable';
|
||||
|
||||
function QorSorter(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorSorter.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorSorter.prototype = {
|
||||
constructor: QorSorter,
|
||||
|
||||
init: function () {
|
||||
this.$element.addClass(CLASS_IS_SORTABLE);
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function () {
|
||||
this.$element.on(EVENT_CLICK, '> thead > tr > th', $.proxy(this.sort, this));
|
||||
},
|
||||
|
||||
unbind: function () {
|
||||
this.$element.off(EVENT_CLICK, this.sort);
|
||||
},
|
||||
|
||||
sort: function (e) {
|
||||
var $target = $(e.currentTarget);
|
||||
var orderBy = $target.data('orderBy');
|
||||
var search = location.search;
|
||||
var param = 'order_by=' + orderBy;
|
||||
|
||||
// Stop when it is not sortable
|
||||
if (!orderBy) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (/order_by/.test(search)) {
|
||||
search = search.replace(/order_by(=\w+)?/, function () {
|
||||
return param;
|
||||
});
|
||||
} else {
|
||||
search += search.indexOf('?') > -1 ? ('&' + param) : param;
|
||||
}
|
||||
|
||||
location.search = search;
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.unbind();
|
||||
this.$element.removeClass(CLASS_IS_SORTABLE).removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorSorter.DEFAULTS = {};
|
||||
|
||||
QorSorter.plugin = function (options) {
|
||||
return this.each(function () {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorSorter(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction(fn = data[options])) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function () {
|
||||
var selector = '.qor-js-table';
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function (e) {
|
||||
QorSorter.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function (e) {
|
||||
QorSorter.plugin.call($(selector, e.target));
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorSorter;
|
||||
|
||||
});
|
||||
210
app/views/qor/assets/javascripts/qor/qor-tabs.js
Normal file
210
app/views/qor/assets/javascripts/qor/qor-tabs.js
Normal file
@@ -0,0 +1,210 @@
|
||||
(function (factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var _ = window._;
|
||||
var $body = $('body');
|
||||
var NAMESPACE = 'qor.tabbar';
|
||||
var EVENT_ENABLE = 'enable.' + NAMESPACE;
|
||||
var EVENT_DISABLE = 'disable.' + NAMESPACE;
|
||||
var EVENT_CLICK = 'click.' + NAMESPACE;
|
||||
var CLASS_TAB = '.qor-layout__tab-button';
|
||||
var CLASS_TAB_CONTENT = '.qor-layout__tab-content';
|
||||
var CLASS_TAB_BAR = '.mdl-layout__tab-bar-container';
|
||||
var CLASS_TAB_BAR_RIGHT = '.qor-layout__tab-right';
|
||||
var CLASS_TAB_BAR_LEFT = '.qor-layout__tab-left';
|
||||
var CLASS_ACTIVE = 'is-active';
|
||||
|
||||
function QorTab(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, QorTab.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorTab.prototype = {
|
||||
constructor: QorTab,
|
||||
|
||||
init: function () {
|
||||
this.initTab();
|
||||
this.bind();
|
||||
},
|
||||
|
||||
bind: function () {
|
||||
this.$element.on(EVENT_CLICK, CLASS_TAB, this.switchTab.bind(this));
|
||||
this.$element.on(EVENT_CLICK, CLASS_TAB_BAR_RIGHT, this.scrollTabRight.bind(this));
|
||||
this.$element.on(EVENT_CLICK, CLASS_TAB_BAR_LEFT, this.scrollTabLeft.bind(this));
|
||||
},
|
||||
|
||||
unbind: function () {
|
||||
this.$element.off(EVENT_CLICK, CLASS_TAB, this.switchTab);
|
||||
this.$element.off(EVENT_CLICK, CLASS_TAB_BAR_RIGHT, this.scrollTabRight);
|
||||
this.$element.off(EVENT_CLICK, CLASS_TAB_BAR_LEFT, this.scrollTabLeft);
|
||||
},
|
||||
|
||||
initTab: function () {
|
||||
var data = this.$element.data();
|
||||
|
||||
if (!data.scopeActive) {
|
||||
$(CLASS_TAB).first().addClass(CLASS_ACTIVE);
|
||||
$body.data('tabScopeActive',$(CLASS_TAB).first().data('name'));
|
||||
} else {
|
||||
$body.data('tabScopeActive',data.scopeActive);
|
||||
}
|
||||
|
||||
this.tabWidth = 0;
|
||||
this.slideoutWidth = $(CLASS_TAB_CONTENT).outerWidth();
|
||||
|
||||
_.each($(CLASS_TAB), function(ele) {
|
||||
this.tabWidth = this.tabWidth + $(ele).outerWidth();
|
||||
}.bind(this));
|
||||
|
||||
if (this.tabWidth > this.slideoutWidth) {
|
||||
this.$element.find(CLASS_TAB_BAR).append(QorTab.ARROW_RIGHT);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
scrollTabLeft: function (e) {
|
||||
e.stopPropagation();
|
||||
|
||||
var $scrollBar = $(CLASS_TAB_BAR),
|
||||
scrollLeft = $scrollBar.scrollLeft(),
|
||||
jumpDistance = scrollLeft - this.slideoutWidth;
|
||||
|
||||
if (scrollLeft > 0){
|
||||
$scrollBar.animate({scrollLeft:jumpDistance}, 400, function () {
|
||||
|
||||
$(CLASS_TAB_BAR_RIGHT).show();
|
||||
if ($scrollBar.scrollLeft() == 0) {
|
||||
$(CLASS_TAB_BAR_LEFT).hide();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
scrollTabRight: function (e) {
|
||||
e.stopPropagation();
|
||||
|
||||
var $scrollBar = $(CLASS_TAB_BAR),
|
||||
scrollLeft = $scrollBar.scrollLeft(),
|
||||
tabWidth = this.tabWidth,
|
||||
slideoutWidth = this.slideoutWidth,
|
||||
jumpDistance = scrollLeft + slideoutWidth;
|
||||
|
||||
if (jumpDistance < tabWidth){
|
||||
$scrollBar.animate({scrollLeft:jumpDistance}, 400, function () {
|
||||
|
||||
$(CLASS_TAB_BAR_LEFT).show();
|
||||
if ($scrollBar.scrollLeft() + slideoutWidth >= tabWidth) {
|
||||
$(CLASS_TAB_BAR_RIGHT).hide();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
!$(CLASS_TAB_BAR_LEFT).length && this.$element.find(CLASS_TAB_BAR).prepend(QorTab.ARROW_LEFT);
|
||||
}
|
||||
},
|
||||
|
||||
switchTab: function (e) {
|
||||
var $target = $(e.target),
|
||||
$element = this.$element,
|
||||
data = $target.data(),
|
||||
tabScopeActive = $body.data().tabScopeActive,
|
||||
isInSlideout = $('.qor-slideout').is(':visible');
|
||||
|
||||
if (!isInSlideout) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($target.hasClass(CLASS_ACTIVE)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$element.find(CLASS_TAB).removeClass(CLASS_ACTIVE);
|
||||
$target.addClass(CLASS_ACTIVE);
|
||||
|
||||
$.ajax(data.tabUrl, {
|
||||
method: 'GET',
|
||||
dataType: 'html',
|
||||
processData: false,
|
||||
contentType: false,
|
||||
beforeSend: function () {
|
||||
$('.qor-layout__tab-spinner').remove();
|
||||
var $spinner = '<div class="mdl-spinner mdl-js-spinner is-active qor-layout__tab-spinner"></div>';
|
||||
$(CLASS_TAB_CONTENT).hide().before($spinner);
|
||||
window.componentHandler.upgradeElement($('.qor-layout__tab-spinner')[0]);
|
||||
},
|
||||
success: function (html) {
|
||||
$('.qor-layout__tab-spinner').remove();
|
||||
$body.data('tabScopeActive',$target.data('name'));
|
||||
var $content = $(html).find(CLASS_TAB_CONTENT).html();
|
||||
$(CLASS_TAB_CONTENT).show().html($content).trigger('enable');
|
||||
|
||||
},
|
||||
error: function () {
|
||||
$('.qor-layout__tab-spinner').remove();
|
||||
$body.data('tabScopeActive',tabScopeActive);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.unbind();
|
||||
$body.removeData('tabScopeActive');
|
||||
}
|
||||
};
|
||||
|
||||
QorTab.ARROW_RIGHT = '<a href="javascript://" class="qor-layout__tab-right"></a>';
|
||||
QorTab.ARROW_LEFT = '<a href="javascript://" class="qor-layout__tab-left"></a>';
|
||||
|
||||
QorTab.DEFAULTS = {};
|
||||
|
||||
QorTab.plugin = function (options) {
|
||||
return this.each(function () {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (/destroy/.test(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this.data(NAMESPACE, (data = new QorTab(this, options)));
|
||||
}
|
||||
|
||||
if (typeof options === 'string' && $.isFunction(fn = data[options])) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function () {
|
||||
var selector = '[data-toggle="qor.tab"]';
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function (e) {
|
||||
QorTab.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function (e) {
|
||||
QorTab.plugin.call($(selector, e.target));
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorTab;
|
||||
|
||||
});
|
||||
262
app/views/qor/assets/javascripts/qor/qor-timepicker.js
Normal file
262
app/views/qor/assets/javascripts/qor/qor-timepicker.js
Normal file
@@ -0,0 +1,262 @@
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as anonymous module.
|
||||
define(['jquery'], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node / CommonJS
|
||||
factory(require('jquery'));
|
||||
} else {
|
||||
// Browser globals.
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
'use strict';
|
||||
|
||||
var NAMESPACE = 'qor.timepicker';
|
||||
var EVENT_ENABLE = 'enable.' + NAMESPACE;
|
||||
var EVENT_DISABLE = 'disable.' + NAMESPACE;
|
||||
var EVENT_CLICK = 'click.' + NAMESPACE;
|
||||
var EVENT_FOCUS = 'focus.' + NAMESPACE;
|
||||
var EVENT_KEYDOWN = 'keydown.' + NAMESPACE;
|
||||
var EVENT_BLUR = 'blur.' + NAMESPACE;
|
||||
var EVENT_CHANGE_TIME = 'selectTime.' + NAMESPACE;
|
||||
|
||||
var CLASS_PARENT = '[data-picker-type]';
|
||||
var CLASS_TIME_SELECTED = '.ui-timepicker-selected';
|
||||
|
||||
function QorTimepicker(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend(true, {}, QorTimepicker.DEFAULTS, $.isPlainObject(options) && options);
|
||||
this.formatDate = null;
|
||||
this.pickerData = this.$element.data();
|
||||
this.parent = this.$element.closest(CLASS_PARENT);
|
||||
this.isDateTimePicker = this.parent.data('picker-type') == 'datetime';
|
||||
this.$targetInput = this.parent.find(this.pickerData.targetInput || (this.isDateTimePicker ? '.qor-datetimepicker__input' : '.qor-datepicker__input'));
|
||||
this.init();
|
||||
}
|
||||
|
||||
QorTimepicker.prototype = {
|
||||
init: function() {
|
||||
if (this.$targetInput.is(':disabled')) {
|
||||
this.$element.remove();
|
||||
return;
|
||||
}
|
||||
this.bind();
|
||||
this.oldValue = this.$targetInput.val();
|
||||
|
||||
var dateNow = new Date();
|
||||
var month = dateNow.getMonth() + 1;
|
||||
var date = dateNow.getDate();
|
||||
|
||||
month = month < 8 ? '0' + month : month;
|
||||
date = date < 10 ? '0' + date : date;
|
||||
|
||||
this.dateValueNow = dateNow.getFullYear() + '-' + month + '-' + date;
|
||||
},
|
||||
|
||||
bind: function() {
|
||||
var pickerOptions = {
|
||||
timeFormat: 'H:i',
|
||||
showOn: null,
|
||||
wrapHours: false,
|
||||
scrollDefault: 'now'
|
||||
};
|
||||
|
||||
if (this.isDateTimePicker) {
|
||||
this.$targetInput
|
||||
.qorTimepicker(pickerOptions)
|
||||
.on(EVENT_CHANGE_TIME, $.proxy(this.changeTime, this))
|
||||
.on(EVENT_BLUR, $.proxy(this.blur, this))
|
||||
.on(EVENT_FOCUS, $.proxy(this.focus, this))
|
||||
.on(EVENT_KEYDOWN, $.proxy(this.keydown, this));
|
||||
}
|
||||
|
||||
this.$element.on(EVENT_CLICK, $.proxy(this.show, this));
|
||||
},
|
||||
|
||||
unbind: function() {
|
||||
this.$element.off(EVENT_CLICK, this.show);
|
||||
|
||||
if (this.isDateTimePicker) {
|
||||
this.$targetInput
|
||||
.off(EVENT_CHANGE_TIME, this.changeTime)
|
||||
.off(EVENT_BLUR, this.blur)
|
||||
.off(EVENT_FOCUS, this.focus)
|
||||
.off(EVENT_KEYDOWN, this.keydown);
|
||||
}
|
||||
},
|
||||
|
||||
focus: function() {},
|
||||
|
||||
blur: function() {
|
||||
var inputValue = this.$targetInput.val();
|
||||
var inputArr = inputValue.split(' ');
|
||||
var inputArrLen = inputArr.length;
|
||||
|
||||
var tempValue;
|
||||
var newDateValue;
|
||||
var newTimeValue;
|
||||
var isDate;
|
||||
var isTime;
|
||||
var splitSym;
|
||||
|
||||
var timeReg = /\d{1,2}:\d{1,2}/;
|
||||
var dateReg = /^\d{4}-\d{1,2}-\d{1,2}/;
|
||||
|
||||
if (!inputValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputArrLen == 1) {
|
||||
if (dateReg.test(inputArr[0])) {
|
||||
newDateValue = inputArr[0];
|
||||
newTimeValue = '00:00';
|
||||
}
|
||||
|
||||
if (timeReg.test(inputArr[0])) {
|
||||
newDateValue = this.dateValueNow;
|
||||
newTimeValue = inputArr[0];
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < inputArrLen; i++) {
|
||||
// check for date && time
|
||||
isDate = dateReg.test(inputArr[i]);
|
||||
isTime = timeReg.test(inputArr[i]);
|
||||
|
||||
if (isDate) {
|
||||
newDateValue = inputArr[i];
|
||||
splitSym = '-';
|
||||
}
|
||||
|
||||
if (isTime) {
|
||||
newTimeValue = inputArr[i];
|
||||
splitSym = ':';
|
||||
}
|
||||
|
||||
tempValue = inputArr[i].split(splitSym);
|
||||
|
||||
for (var j = 0; j < tempValue.length; j++) {
|
||||
if (tempValue[j].length < 2) {
|
||||
tempValue[j] = '0' + tempValue[j];
|
||||
}
|
||||
}
|
||||
|
||||
if (isDate) {
|
||||
newDateValue = tempValue.join(splitSym);
|
||||
}
|
||||
|
||||
if (isTime) {
|
||||
newTimeValue = tempValue.join(splitSym);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.checkDate(newDateValue) && this.checkTime(newTimeValue)) {
|
||||
this.$targetInput.val(newDateValue + ' ' + newTimeValue);
|
||||
this.oldValue = this.$targetInput.val();
|
||||
} else {
|
||||
this.$targetInput.val(this.oldValue);
|
||||
}
|
||||
},
|
||||
|
||||
keydown: function(e) {
|
||||
var keycode = e.keyCode;
|
||||
var keys = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 8, 37, 38, 39, 40, 27, 32, 20, 189, 16, 186, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105];
|
||||
if (keys.indexOf(keycode) == -1) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
checkDate: function(value) {
|
||||
var regCheckDate = /^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{1,2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$/;
|
||||
return regCheckDate.test(value);
|
||||
},
|
||||
|
||||
checkTime: function(value) {
|
||||
var regCheckTime = /^([01]\d|2[0-3]):?([0-5]\d)$/;
|
||||
return regCheckTime.test(value);
|
||||
},
|
||||
|
||||
changeTime: function() {
|
||||
var $targetInput = this.$targetInput;
|
||||
|
||||
var oldValue = this.oldValue;
|
||||
var timeReg = /\d{1,2}:\d{1,2}/;
|
||||
var hasTime = timeReg.test(oldValue);
|
||||
var selectedTime = $targetInput
|
||||
.data()
|
||||
.timepickerObj.list.find(CLASS_TIME_SELECTED)
|
||||
.html();
|
||||
var newValue;
|
||||
|
||||
if (!oldValue) {
|
||||
newValue = this.dateValueNow + ' ' + selectedTime;
|
||||
} else if (hasTime) {
|
||||
newValue = oldValue.replace(timeReg, selectedTime);
|
||||
} else {
|
||||
newValue = oldValue + ' ' + selectedTime;
|
||||
}
|
||||
|
||||
$targetInput.val(newValue);
|
||||
$targetInput.trigger('change');
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if (!this.isDateTimePicker) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.$targetInput.qorTimepicker('show');
|
||||
this.oldValue = this.$targetInput.val();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.unbind();
|
||||
this.$targetInput.qorTimepicker('remove');
|
||||
this.$element.removeData(NAMESPACE);
|
||||
}
|
||||
};
|
||||
|
||||
QorTimepicker.DEFAULTS = {};
|
||||
|
||||
QorTimepicker.plugin = function(option) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data(NAMESPACE);
|
||||
var options;
|
||||
var fn;
|
||||
|
||||
if (!data) {
|
||||
if (!$.fn.qorDatepicker) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (/destroy/.test(option)) {
|
||||
return;
|
||||
}
|
||||
|
||||
options = $.extend(true, {}, $this.data(), typeof option === 'object' && option);
|
||||
$this.data(NAMESPACE, (data = new QorTimepicker(this, options)));
|
||||
}
|
||||
|
||||
if (typeof option === 'string' && $.isFunction((fn = data[option]))) {
|
||||
fn.apply(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var selector = '[data-toggle="qor.timepicker"]';
|
||||
|
||||
$(document)
|
||||
.on(EVENT_DISABLE, function(e) {
|
||||
QorTimepicker.plugin.call($(selector, e.target), 'destroy');
|
||||
})
|
||||
.on(EVENT_ENABLE, function(e) {
|
||||
QorTimepicker.plugin.call($(selector, e.target));
|
||||
})
|
||||
.triggerHandler(EVENT_ENABLE);
|
||||
});
|
||||
|
||||
return QorTimepicker;
|
||||
});
|
||||
2
app/views/qor/assets/javascripts/qor_admin_default.js
Normal file
2
app/views/qor/assets/javascripts/qor_admin_default.js
Normal file
File diff suppressed because one or more lines are too long
65
app/views/qor/assets/javascripts/vendors.js
Normal file
65
app/views/qor/assets/javascripts/vendors.js
Normal file
File diff suppressed because one or more lines are too long
10
app/views/qor/assets/javascripts/vendors/cropper.min.js
vendored
Normal file
10
app/views/qor/assets/javascripts/vendors/cropper.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
app/views/qor/assets/javascripts/vendors/jquery.min.js
vendored
Normal file
2
app/views/qor/assets/javascripts/vendors/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
app/views/qor/assets/javascripts/vendors/jquery.timepicker.min.js
vendored
Normal file
7
app/views/qor/assets/javascripts/vendors/jquery.timepicker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9
app/views/qor/assets/javascripts/vendors/material.min.js
vendored
Normal file
9
app/views/qor/assets/javascripts/vendors/material.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
app/views/qor/assets/javascripts/vendors/moment.min.js
vendored
Normal file
7
app/views/qor/assets/javascripts/vendors/moment.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
app/views/qor/assets/javascripts/vendors/mustache.min.js
vendored
Normal file
1
app/views/qor/assets/javascripts/vendors/mustache.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
app/views/qor/assets/javascripts/vendors/redactor.min.js
vendored
Normal file
7
app/views/qor/assets/javascripts/vendors/redactor.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
10
app/views/qor/assets/javascripts/vendors/redactor.source.min.js
vendored
Normal file
10
app/views/qor/assets/javascripts/vendors/redactor.source.min.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
Redactor Plugins
|
||||
|
||||
This plugin allows users to look through and edit text's HTML source code.
|
||||
Developers may extend and enhance this plugin to bring even more HTML-related source code features to the users.
|
||||
This plugin is only compatible with Redactor II 1.0.1 or higher.
|
||||
|
||||
https://imperavi.com/redactor/plugins/source/
|
||||
*/
|
||||
!function(t){t.Redactor.prototype.source=function(){return{init:function(){var e=this.button.addFirst("html","HTML");this.button.addCallback(e,this.source.toggle);var s={width:"100%",margin:"0",background:"#111","box-sizing":"border-box",color:"rgba(255, 255, 255, .8)","font-size":"14px",outline:"none",padding:"16px","line-height":"22px","font-family":'Menlo, Monaco, Consolas, "Courier New", monospace'};this.source.$textarea=t("<textarea />"),this.source.$textarea.css(s).hide(),"textarea"===this.opts.type?this.core.box().append(this.source.$textarea):this.core.box().after(this.source.$textarea),this.core.element().on("destroy.callback.redactor",t.proxy(function(){this.source.$textarea.remove()},this))},toggle:function(){return this.source.$textarea.hasClass("open")?this.source.hide():this.source.show()},setCaretOnShow:function(){this.source.offset=this.offset.get();t(window).scrollTop(),this.core.editor().innerWidth(),this.core.editor().innerHeight();this.source.start=0,this.source.end=0;var e=t("<div/>").append(t.parseHTML(this.core.editor().html(),document,!0)),s=e.find("span.redactor-selection-marker");if(s.length>0){var r=e.html().replace(/&/g,"&");1===s.length?(this.source.start=this.utils.strpos(r,e.find("#selection-marker-1").prop("outerHTML")),this.source.end=this.source.start):2===s.length&&(this.source.start=this.utils.strpos(r,e.find("#selection-marker-1").prop("outerHTML")),this.source.end=this.utils.strpos(r,e.find("#selection-marker-2").prop("outerHTML"))-e.find("#selection-marker-1").prop("outerHTML").toString().length)}},setCaretOnHide:function(t){if(this.source.start=this.source.$textarea.get(0).selectionStart,this.source.end=this.source.$textarea.get(0).selectionEnd,this.source.start>this.source.end&&this.source.end>0){var e=this.source.end,s=this.source.start;this.source.start=e,this.source.end=s}if(this.source.start=this.source.enlargeOffset(t,this.source.start),this.source.end=this.source.enlargeOffset(t,this.source.end),t=t.substr(0,this.source.start)+this.marker.html(1)+t.substr(this.source.start),this.source.end>this.source.start){var r=this.marker.html(1).toString().length;t=t.substr(0,this.source.end+r)+this.marker.html(2)+t.substr(this.source.end+r)}return t},hide:function(){this.source.$textarea.removeClass("open").hide(),this.source.$textarea.off(".redactor-source");var t=this.source.$textarea.val();t=this.paragraphize.load(t),t=this.source.setCaretOnHide(t),this.code.start(t),this.button.enableAll(),this.core.editor().show().focus(),this.selection.restore()},show:function(){this.selection.save(),this.source.setCaretOnShow();var e=this.core.editor().innerHeight(),s=this.code.get();s=s.replace(/\n\n\n/g,"\n"),s=s.replace(/\n\n/g,"\n"),this.core.editor().hide(),this.button.disableAll("html"),this.source.$textarea.val(s).height(e).addClass("open").show(),this.source.$textarea.on("keyup.redactor-source",t.proxy(function(){"textarea"===this.opts.type&&this.core.textarea().val(this.source.$textarea.val())},this)),this.marker.remove(),t(window).scrollTop(scroll),this.source.$textarea[0].setSelectionRange&&this.source.$textarea[0].setSelectionRange(this.source.start,this.source.end),this.source.$textarea[0].scrollTop=0,setTimeout(t.proxy(function(){this.source.$textarea.focus()},this),0)},enlargeOffset:function(t,e){var s=t.length,r=0;if(">"===t[e])r++;else for(var o=e;s>=o&&(r++,">"!==t[o]);o++)if("<"===t[o]||o===s){r=0;break}return e+r}}}}(jQuery);
|
||||
1
app/views/qor/assets/javascripts/vendors/select2.min.js
vendored
Normal file
1
app/views/qor/assets/javascripts/vendors/select2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
app/views/qor/assets/javascripts/vendors/underscore.min.js
vendored
Normal file
6
app/views/qor/assets/javascripts/vendors/underscore.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
app/views/qor/assets/stylesheets/app.css
Normal file
1
app/views/qor/assets/stylesheets/app.css
Normal file
File diff suppressed because one or more lines are too long
BIN
app/views/qor/assets/stylesheets/chosen-sprite.png
Normal file
BIN
app/views/qor/assets/stylesheets/chosen-sprite.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 538 B |
BIN
app/views/qor/assets/stylesheets/chosen-sprite@2x.png
Normal file
BIN
app/views/qor/assets/stylesheets/chosen-sprite@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 738 B |
1
app/views/qor/assets/stylesheets/datetimepicker.css
Normal file
1
app/views/qor/assets/stylesheets/datetimepicker.css
Normal file
File diff suppressed because one or more lines are too long
65
app/views/qor/assets/stylesheets/fonts.css
Normal file
65
app/views/qor/assets/stylesheets/fonts.css
Normal file
@@ -0,0 +1,65 @@
|
||||
@font-face {
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(../fonts/MaterialIcons.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Roboto Italic'), local('Roboto-Italic'), url(//fonts.gstatic.com/s/roboto/v20/KFOkCnqEu92Fr1Mu52xM.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Roboto Light'), local('Roboto-Light'), url(//fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmSU5vAA.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Roboto'), local('Roboto-Regular'), url(//fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Me5g.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
src: local('Roboto Medium'), local('Roboto-Medium'), url(//fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmEU9vAA.woff) format('woff');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Roboto Bold'), local('Roboto-Bold'), url(//fonts.gstatic.com/s/roboto/v20/KFOlCnqEu92Fr1MmWUlvAA.woff) format('woff');
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px; /* Preferred icon size */
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
word-break: normal;
|
||||
|
||||
/* Support for all WebKit browsers. */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
/* Support for Safari and Chrome. */
|
||||
text-rendering: optimizeLegibility;
|
||||
|
||||
/* Support for Firefox. */
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Support for IE. */
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-moz-font-feature-settings: 'liga';
|
||||
font-feature-settings: 'liga';
|
||||
-ms-font-feature-settings: 'liga';
|
||||
}
|
||||
1
app/views/qor/assets/stylesheets/qor.css
Normal file
1
app/views/qor/assets/stylesheets/qor.css
Normal file
File diff suppressed because one or more lines are too long
31
app/views/qor/assets/stylesheets/qor_admin_default.css
Normal file
31
app/views/qor/assets/stylesheets/qor_admin_default.css
Normal file
File diff suppressed because one or more lines are too long
BIN
app/views/qor/assets/stylesheets/redactor-font.eot
Normal file
BIN
app/views/qor/assets/stylesheets/redactor-font.eot
Normal file
Binary file not shown.
53
app/views/qor/assets/stylesheets/scss/_mixins.scss
Normal file
53
app/views/qor/assets/stylesheets/scss/_mixins.scss
Normal file
@@ -0,0 +1,53 @@
|
||||
// Mixins
|
||||
// --------------------------------------------------
|
||||
|
||||
|
||||
// Material Design Lite's mixins
|
||||
@import "mdl/mixins";
|
||||
|
||||
|
||||
// Clearfix
|
||||
//
|
||||
// For modern browsers
|
||||
// 1. The space content is one way to avoid an Opera bug when the
|
||||
// contenteditable attribute is included anywhere else in the document.
|
||||
// Otherwise it causes space to appear at the top and bottom of elements
|
||||
// that are clearfixed.
|
||||
// 2. The use of `table` rather than `block` is only necessary if using
|
||||
// `:before` to contain the top-margins of child elements.
|
||||
//
|
||||
// Source: http://nicolasgallagher.com/micro-clearfix-hack/
|
||||
|
||||
@mixin clearfix() {
|
||||
&:before,
|
||||
&:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
&:after {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin text-hide() {
|
||||
font: 0/0 a;
|
||||
color: transparent;
|
||||
text-shadow: none;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
// Text overflow
|
||||
// Requires inline-block or block for proper styling
|
||||
|
||||
@mixin text-overflow() {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@mixin shadow-2dp-bottom() {
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, $shadow-key-penumbra-opacity),
|
||||
0 3px 1px -2px rgba(0, 0, 0, $shadow-key-umbra-opacity),
|
||||
0 3px 5px 0 rgba(0, 0, 0, $shadow-ambient-shadow-opacity);
|
||||
}
|
||||
19
app/views/qor/assets/stylesheets/scss/_variables.scss
Normal file
19
app/views/qor/assets/stylesheets/scss/_variables.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
// Variables
|
||||
// --------------------------------------------------
|
||||
// Material Design Lite's variables
|
||||
@import "mdl/variables"; // Customize palette
|
||||
// Download: https://storage.googleapis.com/code.getmdl.io/1.0.3/material.blue-pink.min.css
|
||||
$color-primary: $palette-light-blue-500;
|
||||
$color-primary-dark: $palette-light-blue-700; // Z-index master list
|
||||
//
|
||||
// Warning: Avoid customizing these values. They're used for a bird's eye view
|
||||
// of components dependent on the z-axis and are designed to all work together.
|
||||
$zindex-fixer: 990;
|
||||
$zindex-slideout: 1000;
|
||||
$zindex-bottomsheets: 1005;
|
||||
$zindex-modal: 1010;
|
||||
$color-main-header: #0277bd;
|
||||
$color-slideout-header: unquote("rgb(#{$color-primary})");
|
||||
$color-dash: #ededed;
|
||||
$color-body-bg: #f5f5f5;
|
||||
$width-bottomsheet: 640px;
|
||||
21
app/views/qor/assets/stylesheets/scss/app.scss
Normal file
21
app/views/qor/assets/stylesheets/scss/app.scss
Normal file
@@ -0,0 +1,21 @@
|
||||
@import 'variables';
|
||||
@import 'mixins';
|
||||
@import 'app/fonts';
|
||||
@import 'app/layout';
|
||||
@import 'app/typography';
|
||||
@import 'app/forms';
|
||||
@import 'app/select';
|
||||
@import 'app/buttons';
|
||||
@import 'app/labels';
|
||||
@import 'app/table';
|
||||
@import 'app/search-center';
|
||||
@import 'app/menu';
|
||||
@import 'app/qor-icons';
|
||||
@import 'app/alerts';
|
||||
@import 'app/actions';
|
||||
@import 'app/sections';
|
||||
@import 'app/pagination';
|
||||
@import 'app/tabs';
|
||||
@import 'app/utilities';
|
||||
@import 'app/mobile';
|
||||
@import 'app/print';
|
||||
131
app/views/qor/assets/stylesheets/scss/app/_actions.scss
Normal file
131
app/views/qor/assets/stylesheets/scss/app/_actions.scss
Normal file
@@ -0,0 +1,131 @@
|
||||
.mdl-button {
|
||||
font-size: 12px;
|
||||
}
|
||||
.qor-actions {
|
||||
float: left;
|
||||
min-height: 32px;
|
||||
margin-right: 8px;
|
||||
padding: 8px 0;
|
||||
@include clearfix();
|
||||
|
||||
&:first-child {
|
||||
.qor-selector {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
.mdl-button {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.qor-selector,
|
||||
.qor-action--text,
|
||||
.qor-action--label,
|
||||
.qor-action--select,
|
||||
.qor-action--button {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.qor-action--select {
|
||||
height: 32px;
|
||||
border: 2px solid unquote('rgb(#{$color-primary})');
|
||||
}
|
||||
|
||||
.qor-action--label {
|
||||
display: inline-block;
|
||||
height: 28px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
font-size: 14px;
|
||||
line-height: 28px;
|
||||
color: unquote('rgba(#{$color-black}, 0.54)');
|
||||
background-color: #fff;
|
||||
border: 2px solid unquote('rgba(#{$color-black}, 0.12)');
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
color: unquote('rgba(#{$color-black}, 0.54)');
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
padding-right: 0;
|
||||
color: unquote('rgba(#{$color-black}, 0.87)');
|
||||
border-color: unquote('rgb(#{$color-primary})');
|
||||
}
|
||||
|
||||
> i {
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
height: 28px;
|
||||
margin-left: 6px;
|
||||
padding-left: 2px;
|
||||
font-size: 16px;
|
||||
line-height: 28px;
|
||||
background-color: unquote('rgb(#{$color-primary})');
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
+ .qor-action--select,
|
||||
+ .qor-action--label {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
.qor-action-bulk-buttons,
|
||||
.qor-action-forms {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
padding: 8px 0;
|
||||
}
|
||||
.qor-action--button {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
|
||||
+ .qor-action--button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.qor-action-bulk-edit {
|
||||
float: left;
|
||||
margin-right: 16px;
|
||||
}
|
||||
.qor-action-forms {
|
||||
padding-bottom: 0;
|
||||
.qor-form__actions {
|
||||
margin-top: 0;
|
||||
}
|
||||
> .qor-action-button {
|
||||
margin-right: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
> .action-form {
|
||||
margin-right: 24px;
|
||||
}
|
||||
> .action-form,
|
||||
.qor-form-section,
|
||||
.qor-form__actions {
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
.qor-form__actions {
|
||||
.mdl-button {
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.qor-action-disabled{
|
||||
background-color: rgba(0,0,0,.12) !important;
|
||||
box-shadow: none !important;
|
||||
color: rgba(0,0,0,.2) !important;
|
||||
}
|
||||
145
app/views/qor/assets/stylesheets/scss/app/_alerts.scss
Normal file
145
app/views/qor/assets/stylesheets/scss/app/_alerts.scss
Normal file
@@ -0,0 +1,145 @@
|
||||
// Alerts
|
||||
// --------------------------------------------------
|
||||
.qor-alert {
|
||||
padding: 24px 48px 0 24px;
|
||||
line-height: 24px;
|
||||
color: unquote('rgb(#{$palette-grey-700})');
|
||||
background-color: unquote('rgb(#{$palette-grey-100})');
|
||||
position: fixed;
|
||||
z-index: 99999;
|
||||
margin: 0;
|
||||
left: 50%;
|
||||
top: 0;
|
||||
transform: translate(-50%, -110%);
|
||||
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
will-change: transform;
|
||||
transition: transform 0.25s cubic-bezier(0.4, 0, 1, 1);
|
||||
|
||||
@include shadow-2dp();
|
||||
@include clearfix();
|
||||
|
||||
&.qor-alert__active {
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
|
||||
> button {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 9px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.qor-alert-message {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.qor-alert--success {
|
||||
color: unquote('rgb(#{$palette-blue-700})');
|
||||
background-color: unquote('rgb(#{$palette-blue-100})');
|
||||
}
|
||||
|
||||
.qor-alert--error {
|
||||
color: unquote('rgb(#{$palette-pink-700})');
|
||||
background-color: unquote('rgb(#{$palette-pink-100})');
|
||||
}
|
||||
|
||||
.mdl-dialog-bg {
|
||||
position: fixed;
|
||||
background: unquote('rgba(#{$color-black}, 0.54)');
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
// Errors
|
||||
// --------------------------------------------------
|
||||
|
||||
.qor-error {
|
||||
background-color: #fff;
|
||||
list-style: none;
|
||||
|
||||
> li {
|
||||
+ li {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
position: relative;
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
padding-left: 32px;
|
||||
line-height: 1.3;
|
||||
font-weight: normal;
|
||||
margin-bottom: 24px;
|
||||
color: unquote('rgb(#{$color-accent})');
|
||||
|
||||
> i {
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: -5px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
> span {
|
||||
padding-right: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.qor-modal {
|
||||
.qor-error {
|
||||
margin: 24px 0;
|
||||
}
|
||||
}
|
||||
|
||||
#dialog {
|
||||
display: none;
|
||||
.mdl-dialog {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: -moz-fit-content;
|
||||
width: -webkit-fit-content;
|
||||
width: fit-content;
|
||||
height: -moz-fit-content;
|
||||
height: -webkit-fit-content;
|
||||
height: fit-content;
|
||||
margin: auto;
|
||||
padding: 1em;
|
||||
background: white;
|
||||
color: black;
|
||||
display: block;
|
||||
z-index: 9999;
|
||||
top: 200px;
|
||||
max-width: 50%;
|
||||
min-width: 30%;
|
||||
border: 2px solid rgba(0, 0, 0, 0.05);
|
||||
.mdl-dialog__content {
|
||||
> p {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin: 0;
|
||||
width: 40px;
|
||||
&.mdl-dialog__message {
|
||||
padding: 2px 0 0 16px;
|
||||
width: calc(100% - 100px);
|
||||
}
|
||||
i {
|
||||
font-size: 32px;
|
||||
color: #ff0600;
|
||||
}
|
||||
}
|
||||
}
|
||||
button[data-type='cancel'] {
|
||||
color: unquote('rgba(#{$color-black}, 0.54)');
|
||||
}
|
||||
.mdl-dialog__content {
|
||||
color: unquote('rgba(#{$color-black}, 0.75)');
|
||||
}
|
||||
}
|
||||
}
|
||||
24
app/views/qor/assets/stylesheets/scss/app/_buttons.scss
Normal file
24
app/views/qor/assets/stylesheets/scss/app/_buttons.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
// Buttons
|
||||
// --------------------------------------------------
|
||||
.qor-button--small {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.qor-button--new,
|
||||
.qor-button--edit {
|
||||
position: fixed;
|
||||
right: 32px;
|
||||
bottom: 32px;
|
||||
z-index: 3;
|
||||
@include shadow-4dp();
|
||||
}
|
||||
|
||||
.qor-button--accent {
|
||||
color: #fff;
|
||||
background-color: unquote("rgb(#{$color-accent})") !important;
|
||||
}
|
||||
|
||||
.qor-button--muted {
|
||||
color: unquote("rgba(#{$color-black}, 0.54)");
|
||||
}
|
||||
72
app/views/qor/assets/stylesheets/scss/app/_fonts.scss
Normal file
72
app/views/qor/assets/stylesheets/scss/app/_fonts.scss
Normal file
@@ -0,0 +1,72 @@
|
||||
// // Fonts
|
||||
// // --------------------------------------------------
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'Roboto';
|
||||
// font-style: normal;
|
||||
// font-weight: 200;
|
||||
// src: local('Roboto Thin'), local('Roboto-Thin'), url(../fonts/Roboto-Thin.ttf) format('truetype');
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'Roboto';
|
||||
// font-style: italic;
|
||||
// font-weight: 200;
|
||||
// src: local('Roboto ThinItalic'), local('Roboto-ThinItalic'), url(../fonts/Roboto-ThinItalic.ttf) format('truetype');
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'Roboto';
|
||||
// font-style: normal;
|
||||
// font-weight: 300;
|
||||
// src: local('Roboto Light'), local('Roboto-Light'), url(../fonts/Roboto-Light.ttf) format('truetype');
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'Roboto';
|
||||
// font-style: italic;
|
||||
// font-weight: 300;
|
||||
// src: local('Roboto LightItalic'), local('Roboto-LightItalic'), url(../fonts/Roboto-LightItalic.ttf) format('truetype');
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'Roboto';
|
||||
// font-style: normal;
|
||||
// font-weight: 400;
|
||||
// src: local('Roboto Regular'), local('Roboto-Regular'), url(../fonts/Roboto-Regular.ttf) format('truetype');
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'Roboto';
|
||||
// font-style: italic;
|
||||
// font-weight: 400;
|
||||
// src: local('Roboto Italic'), local('Roboto-Italic'), url(../fonts/Roboto-Italic.ttf) format('truetype');
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'Roboto';
|
||||
// font-style: normal;
|
||||
// font-weight: 500;
|
||||
// src: local('Roboto Medium'), local('Roboto-Medium'), url(../fonts/Roboto-Medium.ttf) format('truetype');
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'Roboto';
|
||||
// font-style: italic;
|
||||
// font-weight: 500;
|
||||
// src: local('Roboto MediumItalic'), local('Roboto-MediumItalic'), url(../fonts/Roboto-MediumItalic.ttf) format('truetype');
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'Roboto';
|
||||
// font-style: normal;
|
||||
// font-weight: 700;
|
||||
// src: local('Roboto Bold'), local('Roboto-Bold'), url(../fonts/Roboto-Bold.ttf) format('truetype');
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'Roboto';
|
||||
// font-style: italic;
|
||||
// font-weight: 700;
|
||||
// src: local('Roboto BoldItalic'), local('Roboto-BoldItalic'), url(../fonts/Roboto-BoldItalic.ttf) format('truetype');
|
||||
// }
|
||||
471
app/views/qor/assets/stylesheets/scss/app/_forms.scss
Normal file
471
app/views/qor/assets/stylesheets/scss/app/_forms.scss
Normal file
@@ -0,0 +1,471 @@
|
||||
// Forms
|
||||
// --------------------------------------------------
|
||||
// Form
|
||||
// -------------------------
|
||||
.qor-form-container {
|
||||
margin: 24px;
|
||||
}
|
||||
|
||||
.qor-slideout__body .qor-form-container {
|
||||
margin: 24px 0;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.qor-textarea__input {
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
outline: none;
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
line-height: 1.4 !important;
|
||||
}
|
||||
|
||||
.qor-form__actions {
|
||||
margin-top: 24px;
|
||||
@include clearfix();
|
||||
.percent-complete {
|
||||
margin-top: -22px;
|
||||
font-weight: 600;
|
||||
}
|
||||
#qor-submit-loading {
|
||||
float: right;
|
||||
.mdl-spinner {
|
||||
margin: 0 24px 24px 0;
|
||||
}
|
||||
}
|
||||
.mdl-button {
|
||||
float: right;
|
||||
+ .mdl-button {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mdl-switch {
|
||||
width: auto;
|
||||
&.is-upgraded {
|
||||
padding-right: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.mdl-textfield textarea.mdl-textfield__input {
|
||||
overflow: initial !important;
|
||||
}
|
||||
|
||||
// Search Form
|
||||
// -------------------------
|
||||
.qor-search-container {
|
||||
float: right;
|
||||
z-index: 1;
|
||||
background-color: inherit;
|
||||
}
|
||||
|
||||
// Add to .qor-page__header
|
||||
.qor-search,
|
||||
.qor-global-search {
|
||||
height: 56px;
|
||||
padding: 14px 6px;
|
||||
.mdl-button {
|
||||
top: 10px;
|
||||
}
|
||||
.qor-search__label {
|
||||
color: unquote('rgba(#{$color-black}, 0.54)');
|
||||
}
|
||||
.qor-search__input {
|
||||
font-size: 14px;
|
||||
}
|
||||
.qor-search__clear {
|
||||
display: none;
|
||||
}
|
||||
&.is-dirty,
|
||||
&.is-focused {
|
||||
.qor-search__label {
|
||||
&,
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
.qor-search__clear {
|
||||
right: -7px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Field
|
||||
// -------------------------
|
||||
.qor-field__label {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
font-size: 12px;
|
||||
color: unquote('rgba(#{$color-black}, 0.54)');
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.qor-field__value {
|
||||
display: block;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.qor-field__input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.qor-field__block {
|
||||
display: block;
|
||||
padding-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.chosen-container {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.qor-field__show {
|
||||
display: none;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
[data-upgraded] > .qor-field__show {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.qor-page__show {
|
||||
.qor-field__show {
|
||||
display: block;
|
||||
}
|
||||
.qor-field__edit,
|
||||
.qor-fieldset__delete,
|
||||
.qor-fieldset__add {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.qor-page__edit {
|
||||
.qor-field__show {
|
||||
display: none;
|
||||
}
|
||||
.qor-field__edit,
|
||||
.qor-fieldset__delete {
|
||||
display: block;
|
||||
}
|
||||
.qor-fieldset__add {
|
||||
display: inline-block;
|
||||
&.mdl-menu__item {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.qor-fieldset__add,
|
||||
.qor-fieldset__add-promotion,
|
||||
.qor-button__icon-add {
|
||||
font-size: 12px;
|
||||
padding: 0;
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus:not(:active) {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
|
||||
.qor-field {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
&.is-error {
|
||||
margin-bottom: 16px;
|
||||
.qor-field__label,
|
||||
.qor-field__error,
|
||||
.mdl-textfield__label {
|
||||
color: unquote('rgb(#{$color-accent})');
|
||||
}
|
||||
.qor-field__error {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
> i {
|
||||
font-size: 18px;
|
||||
vertical-align: top;
|
||||
}
|
||||
> span {
|
||||
+ span {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.mdl-textfield,
|
||||
.qor-field__block {
|
||||
+ .qor-field__error {
|
||||
margin-top: -16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.mdl-textfield__label {
|
||||
font-weight: 500;
|
||||
}
|
||||
.mdl-checkbox__label {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom modifier for .mdl-textfield
|
||||
.qor-field {
|
||||
.mdl-textfield__label {
|
||||
color: unquote('rgba(#{$color-black}, 0.54)');
|
||||
font-size: 12px;
|
||||
top: 4px;
|
||||
}
|
||||
.mdl-textfield__input {
|
||||
font-size: 14px;
|
||||
}
|
||||
.mdl-textfield.is-dirty .mdl-textfield__label,
|
||||
.mdl-textfield.has-placeholder .mdl-textfield__label {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.qor-textfield--condensed {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
.mdl-textfield__label {
|
||||
top: 0;
|
||||
font-size: 14px;
|
||||
&:after {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
.mdl-textfield__input {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
// File input
|
||||
// -------------------------
|
||||
.qor-file {
|
||||
position: relative;
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
&.is-svg {
|
||||
.qor-file__list {
|
||||
> ul {
|
||||
> li {
|
||||
text-align: center;
|
||||
img {
|
||||
display: inline-block;
|
||||
max-width: 90%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.qor-file__list {
|
||||
overflow: hidden;
|
||||
> ul {
|
||||
margin-top: 12px;
|
||||
margin-bottom: 0;
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
> li {
|
||||
position: relative;
|
||||
float: left;
|
||||
display: block;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid unquote('rgba(#{$color-black}, 0.12)');
|
||||
background-color: unquote('rgb(#{$color-white})');
|
||||
overflow: hidden;
|
||||
> span {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 5px;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
background-color: unquote('rgba(#{$color-white}, 0.5)');
|
||||
&:first-letter {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
img {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 100%;
|
||||
max-height: 160px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.qor-file__list-item {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 150px;
|
||||
height: 80px;
|
||||
border: 1px solid unquote('rgba(#{$color-black}, 0.12)');
|
||||
background-color: unquote('rgb(#{$color-white})');
|
||||
margin-top: 12px;
|
||||
.qor-file__action {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
padding: 5px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
background-color: unquote('rgba(#{$color-black}, 0.75)');
|
||||
opacity: 0;
|
||||
a {
|
||||
display: inline-block;
|
||||
color: #fff;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-top: 16%;
|
||||
&:hover {
|
||||
i {
|
||||
color: unquote('rgb(#{$color-primary})');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.qor-file__action {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
> span {
|
||||
display: flex;
|
||||
font-size: 12px;
|
||||
align-items: center;
|
||||
line-height: 1.1;
|
||||
height: 80px;
|
||||
img {
|
||||
display: block;
|
||||
margin: auto;
|
||||
max-height: 80px;
|
||||
}
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0 12px;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fieldset
|
||||
// -------------------------
|
||||
.qor-fieldset {
|
||||
position: relative;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
padding: 16px 24px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
background-color: #fff;
|
||||
&.qor-fieldset--new {
|
||||
display: none;
|
||||
}
|
||||
.mdl-textfield__input {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.qor-fieldset {
|
||||
background-color: unquote('rgb(#{$palette-grey-50})');
|
||||
box-shadow: none;
|
||||
border: 1px solid unquote('rgba(#{$color-black}, 0.12)');
|
||||
.qor-fieldset {
|
||||
background-color: #fff;
|
||||
.qor-fieldset {
|
||||
background-color: unquote('rgb(#{$palette-grey-50})');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.qor-fieldset__delete {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.qor-fieldset__alert {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
// Section
|
||||
// ------------------------
|
||||
.qor-section-columns-2 .qor-field {
|
||||
width: 50%;
|
||||
float: left;
|
||||
padding-right: 40px;
|
||||
box-sizing: border-box;
|
||||
.mdl-textfield .qor-field__edit {
|
||||
margin-top: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.qor-section-columns-3 .qor-field {
|
||||
width: 33.33%;
|
||||
float: left;
|
||||
padding-right: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.promotion-coupons .qor-section-columns-3 .qor-field {
|
||||
&:first-child {
|
||||
width: 50%;
|
||||
.mdl-textfield {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
margin-top: 36px;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
width: 60px;
|
||||
.mdl-textfield__label {
|
||||
overflow: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-input-placeholder {
|
||||
/* WebKit, Blink, Edge */
|
||||
color: unquote('rgba(#{$color-black}, 0.12)');
|
||||
}
|
||||
|
||||
::-moz-placeholder {
|
||||
/* Mozilla Firefox 19+ */
|
||||
color: unquote('rgba(#{$color-black}, 0.12)');
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:-ms-input-placeholder {
|
||||
/* Internet Explorer 10-11 */
|
||||
color: unquote('rgba(#{$color-black}, 0.12)');
|
||||
}
|
||||
|
||||
.mdl-checkbox__label {
|
||||
line-height: 22px;
|
||||
}
|
||||
.mdl-checkbox {
|
||||
height: auto;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
102
app/views/qor/assets/stylesheets/scss/app/_icons.scss
Normal file
102
app/views/qor/assets/stylesheets/scss/app/_icons.scss
Normal file
@@ -0,0 +1,102 @@
|
||||
// Icons
|
||||
// --------------------------------------------------
|
||||
|
||||
// http://google.github.io/material-design-icons/#icon-font-for-the-web
|
||||
|
||||
@mixin material-icons{
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px; /* Preferred icon size */
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
word-break: normal;
|
||||
|
||||
/* Support for all WebKit browsers. */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
/* Support for Safari and Chrome. */
|
||||
text-rendering: optimizeLegibility;
|
||||
|
||||
/* Support for Firefox. */
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Support for IE. */
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-moz-font-feature-settings: 'liga';
|
||||
font-feature-settings: 'liga';
|
||||
-ms-font-feature-settings: 'liga';
|
||||
}
|
||||
|
||||
.md,
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px; /* Preferred icon size */
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
word-break: normal;
|
||||
|
||||
/* Support for all WebKit browsers. */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
/* Support for Safari and Chrome. */
|
||||
text-rendering: optimizeLegibility;
|
||||
|
||||
/* Support for Firefox. */
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Support for IE. */
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-moz-font-feature-settings: 'liga';
|
||||
font-feature-settings: 'liga';
|
||||
-ms-font-feature-settings: 'liga';
|
||||
|
||||
// Rules for sizing the icon.
|
||||
&.md-14 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&.md-18 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
&.md-24 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
&.md-36 {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
&.md-48 {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
// Rules for using icons as black on a light background.
|
||||
&.md-dark {
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
}
|
||||
|
||||
&.md-dark.md-inactive {
|
||||
color: rgba(0, 0, 0, 0.26);
|
||||
}
|
||||
|
||||
// Rules for using icons as white on a dark background.
|
||||
&.md-light {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
&.md-light.md-inactive {
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
}
|
||||
19
app/views/qor/assets/stylesheets/scss/app/_labels.scss
Normal file
19
app/views/qor/assets/stylesheets/scss/app/_labels.scss
Normal file
@@ -0,0 +1,19 @@
|
||||
.qor-label {
|
||||
display: inline;
|
||||
padding: 1px 8px;
|
||||
border: 1px solid unquote("rgba(#{$color-black}, 0.12)");
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
background-color: #fff;
|
||||
color: unquote("rgba(#{$color-black}, 0.26)");
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
border-radius: .25em;
|
||||
|
||||
&.is-active {
|
||||
border-color: unquote("rgba(#{$color-primary}, 0.87)");
|
||||
color: unquote("rgb(#{$color-primary})");
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user