├── jbb_layers_panel
├── lp_16.png
├── lp_24.png
├── lps_16.png
├── lps_24.png
├── css
│ ├── img
│ │ ├── eye.png
│ │ ├── eye2.png
│ │ ├── lock.png
│ │ ├── menu.png
│ │ ├── vray.png
│ │ ├── colors.png
│ │ ├── group.png
│ │ ├── group2.png
│ │ ├── layer.png
│ │ ├── layer0.png
│ │ ├── layer2.png
│ │ ├── menu2.png
│ │ ├── radio1.png
│ │ ├── radio2.png
│ │ ├── render.png
│ │ ├── select.png
│ │ ├── square.png
│ │ ├── trash.png
│ │ ├── trash1.png
│ │ ├── trash2.png
│ │ ├── trash3.png
│ │ ├── update.png
│ │ ├── updown.png
│ │ ├── vrayrt.png
│ │ ├── current.png
│ │ ├── fire_16.png
│ │ ├── highlight.png
│ │ ├── history.png
│ │ ├── kt_icon.png
│ │ ├── layer0s.png
│ │ ├── moveSel.png
│ │ ├── newgroup.png
│ │ ├── render2.png
│ │ ├── render3.png
│ │ ├── render_16.png
│ │ ├── studio_16.png
│ │ ├── IndigoLogo.gif
│ │ ├── listButton.png
│ │ ├── maxwell_16.png
│ │ ├── network_16.png
│ │ ├── headerElement.png
│ │ ├── headerElement2.png
│ │ ├── keyshot_icon.png
│ │ ├── headerElementBis.png
│ │ └── headerElement2bis.png
│ ├── norendertoolbar.css
│ ├── warning.css
│ ├── options.css
│ ├── history.css
│ ├── debug.css
│ ├── states.css
│ ├── color.css
│ └── layerspanel.css
├── js
│ ├── images
│ │ ├── Bars.png
│ │ ├── Maps.png
│ │ ├── cut.png
│ │ ├── door.png
│ │ ├── NoColor.png
│ │ ├── picker.gif
│ │ ├── AlphaBar.png
│ │ ├── mappoint.gif
│ │ ├── bar-opacity.png
│ │ ├── map-opacity.png
│ │ ├── rangearrows.gif
│ │ ├── page_white_add.png
│ │ ├── animated-overlay.gif
│ │ ├── page_white_copy.png
│ │ ├── page_white_delete.png
│ │ ├── page_white_edit.png
│ │ ├── page_white_paste.png
│ │ ├── preview-opacity.png
│ │ ├── ui-icons_222222_256x240.png
│ │ ├── ui-icons_2e83ff_256x240.png
│ │ ├── ui-icons_454545_256x240.png
│ │ ├── ui-icons_888888_256x240.png
│ │ ├── ui-icons_cd0a0a_256x240.png
│ │ ├── ui-bg_flat_0_aaaaaa_40x100.png
│ │ ├── ui-bg_flat_75_ffffff_40x100.png
│ │ ├── ui-bg_glass_55_fbf9ee_1x400.png
│ │ ├── ui-bg_glass_65_ffffff_1x400.png
│ │ ├── ui-bg_glass_75_dadada_1x400.png
│ │ ├── ui-bg_glass_75_e6e6e6_1x400.png
│ │ ├── ui-bg_glass_95_fef1ec_1x400.png
│ │ └── ui-bg_highlight-soft_75_cccccc_1x100.png
│ ├── bridge.js
│ ├── jquery.json-2.4.min.js
│ ├── jquery.contextMenu.css
│ ├── jquery.mjs.nestedSortable.old.js
│ ├── jquery.mjs.nestedSortable.js
│ └── states.js
├── z_win32api
│ └── Win32API.so
├── html
│ ├── warning.html
│ ├── color.html
│ ├── history.html
│ ├── debug.html
│ ├── options.html
│ ├── states.html
│ └── Layers Panel.html
├── rb
│ ├── dan-s_and_tig-s_fix_ruby_startup.rb
│ ├── color.rb
│ ├── history.rb
│ ├── warning.rb
│ ├── options.rb
│ ├── menu_toolbar.rb
│ ├── debug.rb
│ ├── methods.rb
│ ├── observers.rb
│ └── states.rb
└── layers_panel.rb
├── README.md
└── jbb_layers_panel.rb
/jbb_layers_panel/lp_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/lp_16.png
--------------------------------------------------------------------------------
/jbb_layers_panel/lp_24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/lp_24.png
--------------------------------------------------------------------------------
/jbb_layers_panel/lps_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/lps_16.png
--------------------------------------------------------------------------------
/jbb_layers_panel/lps_24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/lps_24.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/eye.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/eye.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/eye2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/eye2.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/lock.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/menu.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/vray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/vray.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/colors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/colors.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/group.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/group2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/group2.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/layer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/layer.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/layer0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/layer0.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/layer2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/layer2.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/menu2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/menu2.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/radio1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/radio1.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/radio2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/radio2.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/render.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/render.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/select.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/select.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/square.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/trash.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/trash1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/trash1.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/trash2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/trash2.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/trash3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/trash3.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/update.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/update.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/updown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/updown.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/vrayrt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/vrayrt.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/Bars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/Bars.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/Maps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/Maps.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/cut.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/cut.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/door.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/door.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/current.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/current.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/fire_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/fire_16.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/highlight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/highlight.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/history.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/history.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/kt_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/kt_icon.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/layer0s.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/layer0s.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/moveSel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/moveSel.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/newgroup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/newgroup.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/render2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/render2.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/render3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/render3.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/render_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/render_16.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/studio_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/studio_16.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/NoColor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/NoColor.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/picker.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/picker.gif
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/IndigoLogo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/IndigoLogo.gif
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/listButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/listButton.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/maxwell_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/maxwell_16.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/network_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/network_16.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/AlphaBar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/AlphaBar.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/mappoint.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/mappoint.gif
--------------------------------------------------------------------------------
/jbb_layers_panel/z_win32api/Win32API.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/z_win32api/Win32API.so
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/headerElement.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/headerElement.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/headerElement2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/headerElement2.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/keyshot_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/keyshot_icon.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/bar-opacity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/bar-opacity.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/map-opacity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/map-opacity.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/rangearrows.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/rangearrows.gif
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/headerElementBis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/headerElementBis.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/page_white_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/page_white_add.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/img/headerElement2bis.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/css/img/headerElement2bis.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/animated-overlay.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/animated-overlay.gif
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/page_white_copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/page_white_copy.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/page_white_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/page_white_delete.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/page_white_edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/page_white_edit.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/page_white_paste.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/page_white_paste.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/preview-opacity.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/preview-opacity.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-icons_222222_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-icons_222222_256x240.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-icons_2e83ff_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-icons_2e83ff_256x240.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-icons_454545_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-icons_454545_256x240.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-icons_888888_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-icons_888888_256x240.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-icons_cd0a0a_256x240.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-icons_cd0a0a_256x240.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-bg_flat_0_aaaaaa_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-bg_flat_0_aaaaaa_40x100.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-bg_flat_75_ffffff_40x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-bg_flat_75_ffffff_40x100.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-bg_glass_55_fbf9ee_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-bg_glass_55_fbf9ee_1x400.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-bg_glass_65_ffffff_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-bg_glass_65_ffffff_1x400.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-bg_glass_75_dadada_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-bg_glass_75_dadada_1x400.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-bg_glass_75_e6e6e6_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-bg_glass_75_e6e6e6_1x400.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-bg_glass_95_fef1ec_1x400.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-bg_glass_95_fef1ec_1x400.png
--------------------------------------------------------------------------------
/jbb_layers_panel/js/images/ui-bg_highlight-soft_75_cccccc_1x100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomas-hauchecorne/Layers-Panel/HEAD/jbb_layers_panel/js/images/ui-bg_highlight-soft_75_cccccc_1x100.png
--------------------------------------------------------------------------------
/jbb_layers_panel/css/norendertoolbar.css:
--------------------------------------------------------------------------------
1 |
2 | .rendering{
3 | display: none;
4 | }
5 |
6 | #renderBar {
7 | display: none;
8 | }
9 |
10 | #utilitiesBar{
11 | border-top: 1px solid #efefef;
12 | }
13 |
14 | body {
15 | margin-top: 28px;
16 | }
17 |
18 | #menu {
19 | top: 34px;
20 | }
--------------------------------------------------------------------------------
/jbb_layers_panel/css/warning.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | margin: 0;
4 | color: #000;
5 | background-color: #ff9962;
6 | font-size: 11px;
7 | font-family: arial;
8 | padding: 3px;
9 | overflow-x: hidden;
10 | overflow-y: hidden;
11 | }
12 |
13 | a{
14 | color: #555;
15 | text-decoration: underline;
16 | }
17 |
18 | #more{
19 | float: right;
20 | margin: 2px 5px 0 0;
21 | }
22 |
23 | #hide{
24 | margin-top: 2px;
25 | color: #555;
26 | float: left;
27 | }
28 |
29 | #hide span{
30 | position: relative;
31 | top: -2px;
32 | }
--------------------------------------------------------------------------------
/jbb_layers_panel/css/options.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | margin: 0;
4 | color: #000;
5 | background-color: #ededed;
6 | font-size: 11px;
7 | font-family: arial;
8 | padding: 5px;
9 | overflow-x: hidden;
10 | overflow-y: hidden;
11 | }
12 |
13 | fieldset{
14 | padding: 7px;
15 | margin: 0 0 10px 0;
16 | }
17 |
18 | label{
19 | position: relative;
20 | top: -2px;
21 | }
22 |
23 | #submitButtons{
24 | width: 170px;
25 | margin: auto;
26 | }
27 |
28 | .submitButton{
29 | width: 75px;
30 | margin-right: 5px;
31 | margin-left: 5px;
32 | }
33 |
--------------------------------------------------------------------------------
/jbb_layers_panel/css/history.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | height: 100%;
4 | margin: 0;
5 | color: #000;
6 | background-color: #ededed;
7 | font-size: 11px;
8 | font-family: arial;
9 | padding: 5px;
10 | }
11 |
12 | .warning{
13 | background-color: orange;
14 | }
15 |
16 | a:visited{
17 | color: #000;
18 | }
19 |
20 | fieldset{
21 | padding: 7px;
22 | margin: 0 0 10px 0;
23 | }
24 |
25 | label{
26 | position: relative;
27 | top: -2px;
28 | }
29 |
30 | #items{
31 | height: 100%;
32 | overflow-y: scroll;
33 | border: 1px solid #888;
34 | padding: 5px;
35 | }
36 |
37 | .item{
38 | margin: 1px 0 1px 0;
39 | }
--------------------------------------------------------------------------------
/jbb_layers_panel/css/debug.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | margin: 0;
4 | color: #000;
5 | background-color: #ededed;
6 | font-size: 11px;
7 | font-family: arial;
8 | padding: 5px;
9 | }
10 |
11 | .warning{
12 | background-color: orange;
13 | }
14 |
15 | a:visited{
16 | color: #000;
17 | }
18 |
19 | fieldset{
20 | padding: 7px;
21 | margin: 0 0 10px 0;
22 | }
23 |
24 | label{
25 | position: relative;
26 | top: -2px;
27 | }
28 |
29 | #items{
30 | height: 200px;
31 | overflow-y: scroll;
32 | border: 1px solid #888;
33 | padding: 5px;
34 | }
35 |
36 | #itemsIDs2{
37 | display: none;
38 | background-color: orange;
39 | }
--------------------------------------------------------------------------------
/jbb_layers_panel/js/bridge.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /////////////// JS-RUBY BRIDGE /////////////
5 |
6 |
7 | var callback_busy = false;
8 | var callback_queue = [];
9 | function skpCallback(callback) {
10 | //console.log('skpCallback', callback);
11 | callback_queue.push(callback);
12 | skpPumpCallback();
13 | }
14 |
15 | function skpPumpCallback() {
16 | //console.log('> skpPumpCallback');
17 | if (!callback_busy && callback_queue.length > 0) {
18 | var callback = callback_queue.shift();
19 | skpPushCallback(callback);
20 | }
21 | }
22 |
23 | function skpPushCallback(callback) {
24 | //console.log('>> skpPushCallback', callback);
25 | callback_busy = true;
26 | window.location = callback;
27 | }
28 |
29 | // Called from Ruby.
30 | function skpCallbackReceived() {
31 | //console.log('> skpCallbackReceived');
32 | callback_busy = false;
33 | skpPumpCallback();
34 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Layers Panel
2 | ============
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
17 | THE SOFTWARE.
18 |
--------------------------------------------------------------------------------
/jbb_layers_panel/html/warning.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | WARNING : You're not drawing geometry on Layer0
12 |
13 |
14 |
15 |
16 |
27 |
28 |
--------------------------------------------------------------------------------
/jbb_layers_panel/css/states.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | margin: 5px 0 30px 0;
4 | }
5 |
6 |
7 | #trash {
8 | background: url("./img/trash.png") no-repeat 6px 0px;
9 | }
10 |
11 | #newState {
12 | background: url("./img/layer.png") no-repeat 6px 0px;
13 | width: 25px;
14 | height: 100%;
15 | float: right;
16 | }
17 |
18 | #update {
19 | background: url("./img/update.png") no-repeat 5px 1px;
20 | width: 25px;
21 | height: 100%;
22 | float: right;
23 | }
24 |
25 | #statesContainer {
26 | position: fixed;
27 | width: 200%;
28 | margin: -20px;
29 | }
30 |
31 | #statesContainer2 {
32 | height: 100%;
33 | width: 50%;
34 | margin: 20px;
35 | padding: 0 0 30px 0;
36 | overflow-y: scroll;
37 | }
38 |
39 | #states {
40 | height: 100%;
41 | }
42 |
43 | .state {
44 | margin: 0;
45 | padding: 0;
46 | }
47 |
48 | .stateName {
49 | display: block;
50 | padding: 3px 0 0 8px;
51 | height: 22px;
52 | overflow: hidden;
53 | white-space: nowrap;
54 | text-overflow: ellipsis;
55 | }
56 |
57 | #state_0{
58 | margin: 0;
59 | padding: 0;
60 | list-style-type: none;
61 | }
62 |
63 | #state_0 > .lidiv {
64 | background: url("./img/layer0.png");
65 | padding: 0;
66 | }
--------------------------------------------------------------------------------
/jbb_layers_panel/html/color.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
37 |
38 |
--------------------------------------------------------------------------------
/jbb_layers_panel/rb/dan-s_and_tig-s_fix_ruby_startup.rb:
--------------------------------------------------------------------------------
1 | # encoding UTF-8
2 | #
3 | # !_fix_ruby_startup2-1.rb
4 | #
5 | # ver : 2.0.0 by Dan Rathbun
6 | # ver : 2.1.0 by TIG: It now changes the two Ruby-Lib paths' drive to match
7 | # the Tools-path's drive: thus allowing for SketchUp.exe installation that's
8 | # NOT on system drive to be fixed, when initial opened SKP is not on the same
9 | # drive as Sketchup.exe.
10 | # It also allows for several custom-plugins folders preceeding Ruby-Libs.
11 | # ver : 2.1.1 by TIG: v14 specific trap added.
12 |
13 | #
14 | # Drop in "Tools" folder - for v2014 MR0 use only...
15 | #
16 |
17 | if RUBY_PLATFORM !~ /darwin/i && Sketchup.version == "14.0.4900"
18 |
19 | fix = false
20 |
21 | tdrve = $:.grep(/\/Tools$/)[0][0,2] # the Tools path
22 | rlibs = $:.grep(/\/Tools\/RubyStdLib/) # the 2 Ruby-Lib paths
23 |
24 | rlibs.each{|rlib|
25 |
26 | i = $:.index(rlib)
27 |
28 | if $:[i][0,2] != tdrve # wrong drive letter !
29 |
30 | fix = true
31 | $:[i] = tdrve + $:[i][2..-1] # replaces Ruby-Lib drive with Tool's drive
32 |
33 | end
34 |
35 | }
36 |
37 | if fix
38 |
39 | $".unshift('enumerator.so') unless $".include?('enumerator.so')
40 |
41 | scripts = [
42 | 'enc/encdb.so',
43 | 'enc/iso_8859_1.so',
44 | 'enc/trans/transdb.so',
45 | 'rubygems.rb' # --> 'rbconfig.rb' & all rubygems files
46 | ]
47 |
48 | for script in scripts do
49 | require(script)
50 | end
51 |
52 | end # fix
53 |
54 | end # if not running on Mac OR v2014
55 |
--------------------------------------------------------------------------------
/jbb_layers_panel/rb/color.rb:
--------------------------------------------------------------------------------
1 |
2 | module JBB_LayersPanel
3 | if RUBY_VERSION.to_i >= 2
4 |
5 | ### OPTIONS DIALOG ### ------------------------------------------------------
6 |
7 | # Create the WebDialog instance
8 | def self.createDialogColor
9 | @dialogColor = WebdialogBridge.new("Layers Panel color picker", false, "LayersPanelColor", 250, 100, 300, 200, false)
10 | @dialogColor.set_size(555,360)
11 | @dialogColor.set_file(@html_path5)
12 |
13 |
14 | @dialogColor.add_bridge_callback("setLayerColor") do |wdl, json|
15 | @model.start_operation("Change layer color", true)
16 | hash = self.jsonToHash(json)
17 | layerID = hash['layerID']
18 | @layers.each{|layer|
19 | if layer.get_attribute("jbb_layerspanel", "ID").to_i == layerID.to_i
20 | layer.color = Sketchup::Color.new(hash['red'], hash['green'], hash['blue'])
21 | break
22 | end#if
23 | }
24 | self.getLayerColors()
25 | self.close_layerspanel_dlg_color
26 | @model.commit_operation
27 | end#callback
28 |
29 | @dialogColor.add_bridge_callback("closeDialog") do |wdl, json|
30 | self.close_layerspanel_dlg_color
31 | end#callback
32 | end#def
33 |
34 | def self.show_layerspanel_dlg_color
35 | if !@dialogColor || !@dialogColor.visible?
36 | self.createDialogColor
37 | self.showDialog(@dialogColor)
38 | self.make_toolwindow_frame("Layers Panel color picker")
39 | @dialogColor.execute_script("window.blur()")
40 | end#if
41 | end#def
42 |
43 | def self.close_layerspanel_dlg_color
44 | if @dialogColor && @dialogColor.visible?
45 | @dialogColor.close
46 | end#if
47 | end#def
48 |
49 | end#if
50 | end#module
--------------------------------------------------------------------------------
/jbb_layers_panel/rb/history.rb:
--------------------------------------------------------------------------------
1 |
2 | module JBB_LayersPanel
3 |
4 |
5 | ### OPTIONS DIALOG ### ------------------------------------------------------
6 |
7 | # Create the WebDialog instance
8 | def self.createDialogHistory
9 | @dialogHistory = WebdialogBridge.new("Layers Panel history", false, "LayersPanelHistory", 330, 350, 300, 200, true)
10 | @dialogHistory.min_width = 330
11 | @dialogHistory.set_file(@html_path7)
12 | @serialized_history = nil
13 |
14 | @dialogHistory.add_bridge_callback("getItems") do |wdl, startup|
15 | @serialized_history = @model.get_attribute("jbb_layerspanel", "serialized_history")
16 | if @serialized_history
17 | i = 0
18 | @serialized_history.each{|item|
19 | addItem = "addItem('#{i}', '#{item[0]}', '#{item[1]}', '#{item[2]}');"
20 | @dialogHistory.execute_script(addItem)
21 | i += 1
22 | }
23 | end#if
24 | end#callback
25 |
26 | @dialogHistory.add_bridge_callback("setSerialized") do |wdl, id|
27 | if @serialized_history
28 | serialized = @serialized_history[id.to_i][1]
29 | @serialized_history.shift if @serialized_history.length >= 200
30 | @serialized_history << [Time.now.strftime("%Y-%m-%d %H:%M:%S"), serialized, "History"]
31 | @model.start_operation("Layers Panel history", true)
32 | self.set_attribute(@model, "jbb_layerspanel", "serialized", serialized)
33 | self.set_attribute(@model, "jbb_layerspanel", "serialized_history", @serialized_history)
34 | self.refreshDialog
35 | @model.commit_operation
36 | end#if
37 | end#callback
38 | end#def
39 |
40 | def self.show_layerspanel_dlg_history
41 | if !@dialogHistory || !@dialogHistory.visible?
42 | self.createDialogHistory
43 | self.showDialog(@dialogHistory)
44 | self.make_toolwindow_frame("Layers Panel history")
45 | @dialogHistory.execute_script("window.blur()")
46 | end#if
47 | end#def
48 |
49 | def self.close_layerspanel_dlg_history
50 | if @dialogHistory && @dialogHistory.visible?
51 | @dialogHistory.close
52 | end#if
53 | end#def
54 |
55 |
56 | end#module
--------------------------------------------------------------------------------
/jbb_layers_panel/rb/warning.rb:
--------------------------------------------------------------------------------
1 |
2 | module JBB_LayersPanel
3 |
4 |
5 | ### WARNING LAYER0 ### ------------------------------------------------------
6 |
7 | # Create the WebDialog instance
8 | def self.createDialogWarning
9 | @dialogWarning = UI::WebDialog.new("Layers Panel Warning", false, "LayersPanelWarning", 265, 65, 300, 200, false)
10 | @dialogWarning.set_size(265,65)
11 | # @html_path2 = File.dirname( __FILE__ ) + "/warning.html"
12 | @dialogWarning.set_file(@html_path2)
13 |
14 | @dialogWarning.add_action_callback("displayWarning") do |wdl, display|
15 | # puts display
16 | if display == "true"
17 | Sketchup.write_default("jbb_layers_panel", "display_warning", true)
18 | else
19 | Sketchup.write_default("jbb_layers_panel", "display_warning", false)
20 | end
21 | end#callback
22 | end#def
23 |
24 | def self.show_layerspanel_dlg_warning
25 | if !@dialogWarning || !@dialogWarning.visible?
26 | self.createDialogWarning
27 | self.showDialog(@dialogWarning)
28 | self.make_toolwindow_frame("Layers Panel Warning")
29 | @dialogWarning.execute_script("window.blur()")
30 | end#if
31 | end#def
32 |
33 | def self.close_layerspanel_dlg_warning
34 | if @dialogWarning && @dialogWarning.visible?
35 | @dialogWarning.close
36 | end#if
37 | end#def
38 |
39 | class JBB_LP_ToolsObserver < Sketchup::ToolsObserver
40 | def onActiveToolChanged(tools, tool_name, tool_id)
41 | if Sketchup.read_default("jbb_layers_panel", "display_warning") != false
42 | # puts "onActiveToolChanged: " + tool_name.to_s
43 | if tool_name != "CameraOrbitTool"
44 | if tool_name == "SketchTool" || tool_name == "RectangleTool" || tool_name == "CircleTool" || tool_name == "ArcTool" || tool_name == "PolyTool" || tool_name == "FreehandTool"
45 | if Sketchup.active_model.active_layer != Sketchup.active_model.layers[0]
46 | JBB_LayersPanel.show_layerspanel_dlg_warning
47 | end#if
48 | else
49 | JBB_LayersPanel.close_layerspanel_dlg_warning
50 | end#if
51 | end#if
52 | end#if
53 | end
54 | end
55 | @model.tools.add_observer(JBB_LP_ToolsObserver.new)
56 |
57 |
58 | end#module
--------------------------------------------------------------------------------
/jbb_layers_panel/html/history.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
20 |
21 |
22 |
23 |
67 |
68 |
--------------------------------------------------------------------------------
/jbb_layers_panel/html/debug.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
19 |
20 |
21 |
61 |
62 |
--------------------------------------------------------------------------------
/jbb_layers_panel/js/jquery.json-2.4.min.js:
--------------------------------------------------------------------------------
1 | /*! jQuery JSON plugin 2.4.0 | code.google.com/p/jquery-json */
2 | (function($){'use strict';var escape=/["\\\x00-\x1f\x7f-\x9f]/g,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},hasOwn=Object.prototype.hasOwnProperty;$.toJSON=typeof JSON==='object'&&JSON.stringify?JSON.stringify:function(o){if(o===null){return'null';}
3 | var pairs,k,name,val,type=$.type(o);if(type==='undefined'){return undefined;}
4 | if(type==='number'||type==='boolean'){return String(o);}
5 | if(type==='string'){return $.quoteString(o);}
6 | if(typeof o.toJSON==='function'){return $.toJSON(o.toJSON());}
7 | if(type==='date'){var month=o.getUTCMonth()+1,day=o.getUTCDate(),year=o.getUTCFullYear(),hours=o.getUTCHours(),minutes=o.getUTCMinutes(),seconds=o.getUTCSeconds(),milli=o.getUTCMilliseconds();if(month<10){month='0'+month;}
8 | if(day<10){day='0'+day;}
9 | if(hours<10){hours='0'+hours;}
10 | if(minutes<10){minutes='0'+minutes;}
11 | if(seconds<10){seconds='0'+seconds;}
12 | if(milli<100){milli='0'+milli;}
13 | if(milli<10){milli='0'+milli;}
14 | return'"'+year+'-'+month+'-'+day+'T'+
15 | hours+':'+minutes+':'+seconds+'.'+milli+'Z"';}
16 | pairs=[];if($.isArray(o)){for(k=0;k
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
16 |
22 |
26 |
27 |
28 |
29 |
30 |
78 |
79 |
--------------------------------------------------------------------------------
/jbb_layers_panel/html/states.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Last model state
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/jbb_layers_panel/rb/options.rb:
--------------------------------------------------------------------------------
1 |
2 | module JBB_LayersPanel
3 |
4 |
5 | ### OPTIONS DIALOG ### ------------------------------------------------------
6 |
7 | # Create the WebDialog instance
8 | def self.createDialogOptions
9 | @dialogOptions = WebdialogBridge.new("Layers Panel options", false, "LayersPanelOptions", 250, 100, 300, 200, false)
10 | @dialogOptions.set_size(270,280)
11 | @dialogOptions.set_file(@html_path3)
12 |
13 | @dialogOptions.add_bridge_callback("startup") do |wdl, startup|
14 | if startup == "true"
15 | Sketchup.write_default("jbb_layers_panel", "startup", true)
16 | else
17 | Sketchup.write_default("jbb_layers_panel", "startup", false)
18 | end
19 | end#callback
20 |
21 | @dialogOptions.add_bridge_callback("displayWarning") do |wdl, display|
22 | # puts display
23 | if display == "true"
24 | Sketchup.write_default("jbb_layers_panel", "display_warning", true)
25 | else
26 | Sketchup.write_default("jbb_layers_panel", "display_warning", false)
27 | end
28 | end#callback
29 |
30 | @dialogOptions.add_bridge_callback("displayRender") do |wdl, display|
31 | # puts display
32 | if display == "true"
33 | Sketchup.write_default("jbb_layers_panel", "display_render", true)
34 | else
35 | Sketchup.write_default("jbb_layers_panel", "display_render", false)
36 | end
37 | end#callback
38 |
39 | @dialogOptions.add_bridge_callback("autoUpdate") do |wdl, autoUpdate|
40 | # puts autoUpdate
41 | if autoUpdate == "true"
42 | Sketchup.write_default("jbb_layers_panel", "auto_update", true)
43 | begin
44 | self.stopUpdateTimer
45 | rescue
46 | end
47 | else
48 | Sketchup.write_default("jbb_layers_panel", "auto_update", false)
49 | self.startUpdateTimer
50 | end
51 | end#callback
52 |
53 | @dialogOptions.add_bridge_callback("close") do |wdl, display|
54 | JBB_LayersPanel.close_layerspanel_dlg_options
55 | JBB_LayersPanel.dialog.execute_script("reloadDialog();")
56 | end#callback
57 |
58 | @dialogOptions.add_bridge_callback("getOptions") do ||
59 | startup = Sketchup.read_default("jbb_layers_panel", "startup")
60 | displayRender = Sketchup.read_default("jbb_layers_panel", "display_render")
61 | displayWarning = Sketchup.read_default("jbb_layers_panel", "display_warning")
62 | autoUpdate = Sketchup.read_default("jbb_layers_panel", "auto_update")
63 |
64 | if startup == true
65 | @dialogOptions.execute_script("checkStartup()")
66 | end#if
67 | if displayRender == false
68 | @dialogOptions.execute_script("uncheckRender()")
69 | end#if
70 | if displayWarning == false
71 | @dialogOptions.execute_script("uncheckWarning()")
72 | end#if
73 | if autoUpdate == true
74 | @dialogOptions.execute_script("checkUpdate()")
75 | end#if
76 | end#callback
77 | end#def
78 |
79 | def self.show_layerspanel_dlg_options
80 | if !@dialogOptions || !@dialogOptions.visible?
81 | self.createDialogOptions
82 | self.showDialog(@dialogOptions)
83 | self.make_toolwindow_frame("Layers Panel options")
84 | @dialogOptions.execute_script("window.blur()")
85 | end#if
86 | end#def
87 |
88 | def self.close_layerspanel_dlg_options
89 | if @dialogOptions && @dialogOptions.visible?
90 | @dialogOptions.close
91 | end#if
92 | end#def
93 |
94 |
95 | end#module
--------------------------------------------------------------------------------
/jbb_layers_panel/rb/menu_toolbar.rb:
--------------------------------------------------------------------------------
1 |
2 | module JBB_LayersPanel
3 |
4 |
5 |
6 | ### MENU & TOOLBARS ### ------------------------------------------------------
7 |
8 | def self.toggle_layerspanel_dlg
9 | if @dialog && @dialog.visible?
10 | @dialog.close
11 | false
12 | else
13 | self.createDialog
14 | self.showDialog(@dialog, true)
15 | self.make_toolwindow_frame("Layers Panel")
16 | true
17 | end#if
18 | end#def
19 |
20 | def self.toggle_states_dlg
21 | if @dialogStates && @dialogStates.visible?
22 | self.close_layerspanel_dlg_states
23 | false
24 | else
25 | self.show_layerspanel_dlg_states
26 | true
27 | end#if
28 | end#def
29 |
30 | # Thomthom's snippet :
31 | # http://sketchucation.com/forums/viewtopic.php?p=280331#p280331
32 | def self.make_toolwindow_frame(window_title)
33 | if WIN
34 | # Retrieves the window handle to the active window attached to the calling
35 | # thread's message queue.
36 | hwnd = GetActiveWindow.call
37 | return nil if hwnd.nil?
38 |
39 | # Verify window text as extra security to ensure it's the correct window.
40 | buf_len = GetWindowTextLength.call(hwnd)
41 | return nil if buf_len == 0
42 |
43 | str = ' ' * (buf_len + 1)
44 | result = GetWindowText.call(hwnd, str, str.length)
45 | return nil if result == 0
46 |
47 | return nil unless str.strip == window_title.strip
48 |
49 | # Set frame to Toolwindow
50 | style = GetWindowLong.call(hwnd, GWL_EXSTYLE)
51 | return nil if style == 0
52 |
53 | result = SetWindowLong.call(hwnd, GWL_EXSTYLE, style | WS_EX_TOOLWINDOW)
54 | return nil if result == 0
55 |
56 | # Remove and disable minimze and maximize
57 | # http://support.microsoft.com/kb/137033
58 | style = GetWindowLong.call(hwnd, GWL_STYLE)
59 | return nil if style == 0
60 |
61 | style = style & ~WS_MINIMIZEBOX
62 | style = style & ~WS_MAXIMIZEBOX
63 | result = SetWindowLong.call(hwnd, GWL_STYLE, style)
64 | return nil if result == 0
65 |
66 | # Refresh the window frame
67 | result = SetWindowPos.call(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED|SWP_NOSIZE|SWP_NOMOVE)
68 | result != 0
69 | end#if
70 | end#def
71 |
72 | def self.layerspanel_dlg_validation_proc(dialog)
73 | if dialog && dialog.visible?
74 | MF_CHECKED
75 | else
76 | MF_UNCHECKED
77 | end#if
78 | end#def
79 |
80 | unless file_loaded?( __FILE__ )
81 | # Commands
82 | cmd = UI::Command.new( 'Layers Panel' ) { self.toggle_layerspanel_dlg }
83 | cmd.status_bar_text = 'Show or hide the Layers Panel.'
84 | cmd.small_icon = "../lp_16.png"
85 | cmd.large_icon = "../lp_24.png"
86 | cmd.tooltip = 'Layers Panel'
87 | cmd.set_validation_proc { self.layerspanel_dlg_validation_proc(@dialog) }
88 | cmd_toggle_layerspanel_dlg = cmd
89 |
90 | cmd2 = UI::Command.new( 'Layer States' ) { self.toggle_states_dlg }
91 | cmd2.status_bar_text = 'Show or hide the Layer States Panel.'
92 | cmd2.small_icon = "../lps_16.png"
93 | cmd2.large_icon = "../lps_24.png"
94 | cmd2.tooltip = 'Layer States'
95 | cmd2.set_validation_proc { self.layerspanel_dlg_validation_proc(@dialogStates) }
96 | cmd_toggle_states_dlg = cmd2
97 |
98 | window_menu = UI.menu("Window")
99 | lp_menu = window_menu.add_submenu("Layers Panel")
100 | lp_menu.add_item( cmd_toggle_layerspanel_dlg )
101 | lp_menu.add_item( cmd_toggle_states_dlg )
102 | lp_menu.add_item( "Options" ) { JBB_LayersPanel.show_layerspanel_dlg_options }
103 | lp_menu.add_item( "Debug" ) { JBB_LayersPanel.show_layerspanel_dlg_debug }
104 |
105 | layerspanel_tb = UI::Toolbar.new "Layers Panel"
106 | layerspanel_tb.add_item cmd_toggle_layerspanel_dlg
107 | layerspanel_tb.add_item cmd_toggle_states_dlg
108 | if WIN
109 | done = false
110 | timer = UI.start_timer(0, false) {
111 | next if done
112 | done = true
113 | layerspanel_tb.restore
114 | }
115 | end#if
116 | end
117 |
118 |
119 | end#module
--------------------------------------------------------------------------------
/jbb_layers_panel/js/jquery.contextMenu.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery contextMenu - Plugin for simple contextMenu handling
3 | *
4 | * Version: git-master
5 | *
6 | * Authors: Rodney Rehm, Addy Osmani (patches for FF)
7 | * Web: http://medialize.github.com/jQuery-contextMenu/
8 | *
9 | * Licensed under
10 | * MIT License http://www.opensource.org/licenses/mit-license
11 | * GPL v3 http://opensource.org/licenses/GPL-3.0
12 | *
13 | */
14 |
15 | .context-menu-list {
16 | margin:0;
17 | padding:0;
18 |
19 | min-width: 120px;
20 | max-width: 250px;
21 | display: inline-block;
22 | position: absolute;
23 | list-style-type: none;
24 |
25 | border: 1px solid #DDD;
26 | background: #EEE;
27 |
28 | -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
29 | -moz-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
30 | -ms-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
31 | -o-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
32 | box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
33 |
34 | font-family: Verdana, Arial, Helvetica, sans-serif;
35 | font-size: 11px;
36 | }
37 |
38 | .context-menu-item {
39 | padding: 2px 2px 2px 2px;
40 | background-color: #EEE;
41 | position: relative;
42 | -webkit-user-select: none;
43 | -moz-user-select: -moz-none;
44 | -ms-user-select: none;
45 | user-select: none;
46 | }
47 |
48 | .context-menu-separator {
49 | padding-bottom:0;
50 | border-bottom: 1px solid #DDD;
51 | }
52 |
53 | .context-menu-item > label > input,
54 | .context-menu-item > label > textarea {
55 | -webkit-user-select: text;
56 | -moz-user-select: text;
57 | -ms-user-select: text;
58 | user-select: text;
59 | }
60 |
61 | .context-menu-item.hover {
62 | cursor: pointer;
63 | background-color: #39F;
64 | }
65 |
66 | .context-menu-item.disabled {
67 | color: #666;
68 | }
69 |
70 | .context-menu-input.hover,
71 | .context-menu-item.disabled.hover {
72 | cursor: default;
73 | background-color: #EEE;
74 | }
75 |
76 | .context-menu-submenu:after {
77 | content: ">";
78 | color: #666;
79 | position: absolute;
80 | top: 0;
81 | right: 3px;
82 | z-index: 1;
83 | }
84 |
85 | /* icons
86 | #protip:
87 | In case you want to use sprites for icons (which I would suggest you do) have a look at
88 | http://css-tricks.com/13224-pseudo-spriting/ to get an idea of how to implement
89 | .context-menu-item.icon:before {}
90 | */
91 | .context-menu-item.icon { min-height: 18px; background-repeat: no-repeat; background-position: 4px 2px; }
92 | .context-menu-item.icon-edit { background-image: url(images/page_white_edit.png); }
93 | .context-menu-item.icon-cut { background-image: url(images/cut.png); }
94 | .context-menu-item.icon-copy { background-image: url(images/page_white_copy.png); }
95 | .context-menu-item.icon-paste { background-image: url(images/page_white_paste.png); }
96 | .context-menu-item.icon-delete { background-image: url(images/page_white_delete.png); }
97 | .context-menu-item.icon-add { background-image: url(images/page_white_add.png); }
98 | .context-menu-item.icon-quit { background-image: url(images/door.png); }
99 |
100 | /* vertically align inside labels */
101 | .context-menu-input > label > * { vertical-align: top; }
102 |
103 | /* position checkboxes and radios as icons */
104 | .context-menu-input > label > input[type="checkbox"],
105 | .context-menu-input > label > input[type="radio"] {
106 | margin-left: -17px;
107 | }
108 | .context-menu-input > label > span {
109 | margin-left: 5px;
110 | }
111 |
112 | .context-menu-input > label,
113 | .context-menu-input > label > input[type="text"],
114 | .context-menu-input > label > textarea,
115 | .context-menu-input > label > select {
116 | display: block;
117 | width: 100%;
118 |
119 | -webkit-box-sizing: border-box;
120 | -moz-box-sizing: border-box;
121 | -ms-box-sizing: border-box;
122 | -o-box-sizing: border-box;
123 | box-sizing: border-box;
124 | }
125 |
126 | .context-menu-input > label > textarea {
127 | height: 100px;
128 | }
129 | .context-menu-item > .context-menu-list {
130 | display: none;
131 | /* re-positioned by js */
132 | right: -5px;
133 | top: 5px;
134 | }
135 |
136 | .context-menu-item.hover > .context-menu-list {
137 | display: block;
138 | }
139 |
140 | .context-menu-accesskey {
141 | text-decoration: underline;
142 | }
143 |
--------------------------------------------------------------------------------
/jbb_layers_panel/css/color.css:
--------------------------------------------------------------------------------
1 | .jPicker .Icon{display:inline-block;height:24px;position:relative;text-align:left;width:25px}.jPicker .Icon span.Color,.jPicker .Icon span.Alpha{background-position:2px 2px;display:block;height:100%;left:0;position:absolute;top:0;width:100%}.jPicker .Icon span.Image{background-repeat:no-repeat;cursor:pointer;display:block;height:100%;left:0;position:absolute;top:0;width:100%}.jPicker.Container{color:#000;z-index:10}table.jPicker{background-color:#efefef;font-family:Arial,Helvetica,Sans-Serif;font-size:12px!important;margin:0;padding:5px;width:545px;z-index:20}.jPicker .Move{background-color:#ddd;border-color:#fff #666 #666 #fff;border-style:solid;border-width:1px;cursor:move;height:12px;padding:0}.jPicker .Title{font-size:11px!important;font-weight:bold;margin:-2px 0 0 0;padding:10px 0 0 0;text-align:center;width:100%}.jPicker div.Map{border-bottom:2px solid #fff;border-left:2px solid #9a9a9a;border-right:2px solid #fff;border-top:2px solid #9a9a9a;cursor:crosshair;height:260px;margin:0 10px 10px 10px;overflow:hidden;padding:0;position:relative;width:260px}.jPicker div[class="Map"]{height:256px;width:256px}.jPicker div.Bar{border-bottom:2px solid #fff;border-left:2px solid #9a9a9a;border-right:2px solid #fff;border-top:2px solid #9a9a9a;cursor:n-resize;height:260px;margin:12px 10px 0 5px;overflow:hidden;padding:0;position:relative;width:24px}.jPicker div[class="Bar"]{height:256px;width:20px}.jPicker .Map .Map1,.jPicker .Map .Map2,.jPicker .Map .Map3,.jPicker .Bar .Map1,.jPicker .Bar .Map2,.jPicker .Bar .Map3,.jPicker .Bar .Map4,.jPicker .Bar .Map5,.jPicker .Bar .Map6{background-color:transparent;background-image:none;display:block;left:0;position:absolute;top:0}.jPicker .Map .Map1,.jPicker .Map .Map2,.jPicker .Map .Map3{height:2596px;width:256px}.jPicker .Bar .Map1,.jPicker .Bar .Map2,.jPicker .Bar .Map3,.jPicker .Bar .Map4{height:3896px;width:20px}.jPicker .Bar .Map5,.jPicker .Bar .Map6{height:256px;width:20px}.jPicker .Map .Map1,.jPicker .Map .Map2,.jPicker .Bar .Map6{background-repeat:no-repeat}.jPicker .Map .Map3,.jPicker .Bar .Map5{background-repeat:repeat}.jPicker .Bar .Map1,.jPicker .Bar .Map2,.jPicker .Bar .Map3,.jPicker .Bar .Map4{background-repeat:repeat-x}.jPicker .Map .Arrow{display:block;position:absolute}.jPicker .Bar .Arrow{display:block;left:0;position:absolute}.jPicker .Preview{font-size:9px;padding:5px 0 0 0;text-align:center}.jPicker .Preview div{border:2px inset #eee;height:62px;margin:0 auto;padding:0;width:62px}.jPicker .Preview div span{border:1px solid #000;display:block;height:30px;margin:0 auto;padding:0;width:60px}.jPicker .Preview .Active{border-bottom-width:0}.jPicker .Preview .Current{border-top-width:0;cursor:pointer}.jPicker input{font-size:13px}.jPicker .Button{text-align:center;padding:0 4px;width:115px}.jPicker .Button input{padding:2px 0;width:100px}.jPicker .Button .Ok{margin:12px 0 5px 0}.jPicker td{margin:0;padding:0}.jPicker td.Radio{margin:0;padding:0;width:31px}.jPicker td.Radio input{margin:0 5px 0 0;padding:0}.jPicker td.Text{font-size:12px!important;height:22px;margin:0;padding:0;text-align:left;width:70px}.jPicker tr.Hex td.Text{width:100px}.jPicker td.Text input{background-color:#fff;border:1px inset #aaa;height:19px;margin:0 0 0 5px;text-align:left;width:30px}.jPicker td[class="Text"] input{height:15px}.jPicker tr.Hex td.Text input.Hex{width:50px}.jPicker tr.Hex td.Text input.AHex{width:20px}.jPicker .Grid{text-align:center;width:114px}.jPicker .Grid span.QuickColor{border:1px inset #aaa;cursor:pointer;display:inline-block;height:15px;line-height:15px;margin:0;padding:0;width:19px}.jPicker .Grid span[class="QuickColor"]{width:17px}
2 |
3 |
4 | #jPicker { margin: 0px 8px; text-align: left; }
5 | #jPicker ul { font-size: 15px; margin: 0px 0px 0px 15px; padding: 0px; }
6 | #jPicker ul li { list-style: disc; padding: 2px 0px; }
7 | #jPicker ul li ul { margin-bottom: 10px; }
8 | #jPicker ul li ul li { list-style: circle; }
9 | #jPicker p { font-size: 13px; padding: 0px 10px; }
10 | #jPicker hr { clear: both; }
11 | #jPicker h2.jPicker { font-size: 16px; padding: 20px 10px; }
12 | #jPicker code { color: #8bd; font-size: 14px; font-weight: bold; }
13 | #jPicker pre { background: #eee; border: 1px solid #000; color: #000; display: block; font-size: 11px; margin: 10px 5px; padding: 5px; }
14 | #jPicker span { font-size: 13px; text-align: center; }
15 | #jPicker a { color: #ff8050; }
16 | #jPicker input { font-size: 13px; padding: 2px 5px; }
17 | #jPicker h2 { font-size: 16px; margin: 10px 0px; }
18 |
19 |
20 |
21 | body {
22 | margin: 0;
23 | color: #000;
24 | background-color: #ededed;
25 | font-size: 11px;
26 | font-family: arial;
27 | margin: -5px;
28 | overflow:hidden;
29 | }
30 |
31 |
32 |
--------------------------------------------------------------------------------
/jbb_layers_panel/rb/debug.rb:
--------------------------------------------------------------------------------
1 |
2 | module JBB_LayersPanel
3 |
4 |
5 | ### OPTIONS DIALOG ### ------------------------------------------------------
6 |
7 | # Create the WebDialog instance
8 | def self.createDialogDebug
9 | @dialogDebug = WebdialogBridge.new("Layers Panel debug", false, "LayersPanelDebug", 250, 100, 300, 200, false)
10 | @dialogDebug.set_size(350,350)
11 | @dialogDebug.set_file(@html_path4)
12 |
13 | @dialogDebug.add_bridge_callback("getItemsIDs") do |wdl, startup|
14 | getDictID = "getDictID('#{@model.get_attribute("jbb_layerspanel", "layerDictID")}');"
15 | @dialogDebug.execute_script(getDictID)
16 |
17 | #Groups
18 | serialized = @model.get_attribute("jbb_layerspanel", "serialized") #retreive string of serialized items
19 | groups = serialized.to_s.scan(/group\[(\d+)\]\=(\d+|null)/) #find groups, make an array of them
20 | groups.each{|match|
21 | #match[0] : ID
22 | #match[1] : parent ID
23 | match = match.to_a
24 | name = @model.get_attribute("jbb_layerspanel_groups", match[0])
25 | addItem = "addItem('#{name}', '#{match[0]}');"
26 | @dialogDebug.execute_script(addItem)
27 | }
28 |
29 | #Layers
30 | @layers.each{|layer|
31 | addItem = "addItem('#{layer.name}', '#{layer.get_attribute("jbb_layerspanel", "ID")}');"
32 | @dialogDebug.execute_script(addItem)
33 | }
34 | end#callback
35 |
36 | @dialogDebug.add_bridge_callback("debugItemsIDs") do |wdl, action|
37 | @model.start_operation("Debug Layers Panel", true)
38 | puts ""
39 | puts ""
40 | puts "--- LAYERS PANEL DEBUG ---"
41 | puts ""
42 | self.initializeLayerDictID
43 | highestID = 0
44 | serialized = @model.get_attribute("jbb_layerspanel", "serialized") #retreive string of serialized items
45 | groups = serialized.to_s.scan(/group\[(\d+)\]/) #find groups, make an array of them
46 | groups.each{|match| #Groups
47 | if match[0].to_i > highestID.to_i
48 | highestID = match[0].to_i
49 | end#if
50 | }
51 | @layers.each{|layer|
52 | if layer.get_attribute("jbb_layerspanel", "ID") > highestID.to_i
53 | highestID = layer.get_attribute("jbb_layerspanel", "ID")
54 | end#if
55 | }
56 | if @model.get_attribute("jbb_layerspanel", "layerDictID") < highestID.to_i + 1
57 | self.set_attribute(@model, "jbb_layerspanel", "layerDictID", highestID.to_i+1)
58 | @layerDictID = highestID.to_i+1
59 | end#if
60 |
61 | ids = nil
62 | ids = Array.new
63 |
64 | #Fix groups first, because keeping groups order is more important as they can contain other items
65 | serialized = @model.get_attribute("jbb_layerspanel", "serialized") #retreive string of serialized items
66 | serialized.to_s.gsub!(/group\[(\d+)\]/) do |match|
67 | id = match.scan(/group\[(\d+)\]/)[0][0].to_i
68 | name = "Group" #Default
69 | if @model.get_attribute("jbb_layerspanel_groups", id) != nil
70 | name = @model.get_attribute("jbb_layerspanel_groups", id)
71 | end#if
72 | if ids[id] != nil
73 | puts "Fixed ID for \"" + name + "\""
74 | self.incLayerDictID
75 | self.set_attribute(@model, "jbb_layerspanel_groups", @layerDictID, name)
76 | id = @layerDictID
77 | end#if
78 | ids[id] = name
79 | "group[" + id.to_s + "]" #Replace id in serialized string
80 | end
81 | self.set_attribute(@model, "jbb_layerspanel", "serialized", serialized)
82 |
83 | #Then fix layers
84 | @layers.each{|layer|
85 | if ids[layer.get_attribute("jbb_layerspanel", "ID")] != nil
86 | puts "Fixed ID for \"" + layer.name + "\""
87 | self.set_attribute(layer, "jbb_layerspanel", "ID", @layerDictID)
88 | self.incLayerDictID
89 | end#if
90 | ids[layer.get_attribute("jbb_layerspanel", "ID")] = layer.name
91 | }
92 |
93 | @dialogDebug.execute_script("reloadDialog();")
94 | self.dialogStartup #Reload main dialog
95 | puts ""
96 | puts "--- END DEBUG ---"
97 | puts ""
98 | @model.commit_operation
99 | end#callback
100 | end#def
101 |
102 | def self.show_layerspanel_dlg_debug
103 | if !@dialogDebug || !@dialogDebug.visible?
104 | self.createDialogDebug
105 | self.showDialog(@dialogDebug)
106 | self.make_toolwindow_frame("Layers Panel debug")
107 | @dialogDebug.execute_script("window.blur()")
108 | end#if
109 | end#def
110 |
111 | def self.close_layerspanel_dlg_debug
112 | if @dialogDebug && @dialogDebug.visible?
113 | @dialogDebug.close
114 | end#if
115 | end#def
116 |
117 |
118 | end#module
--------------------------------------------------------------------------------
/jbb_layers_panel/layers_panel.rb:
--------------------------------------------------------------------------------
1 | #-----------------------------------------------------------------------------
2 | require 'sketchup.rb'
3 | #-----------------------------------------------------------------------------
4 |
5 | module JBB_LayersPanel
6 |
7 | require 'jbb_layers_panel/rb/dan-s_and_tig-s_fix_ruby_startup.rb'
8 |
9 | #-----------------------------------------------------------------------------
10 |
11 | OSX = ( Object::RUBY_PLATFORM =~ /darwin/i ? true : false )
12 | WIN = ( (Object::RUBY_PLATFORM =~ /mswin/i || Object::RUBY_PLATFORM =~ /mingw/i) ? true : false )
13 |
14 | #-----------------------------------------------------------------------------
15 |
16 | if WIN
17 |
18 | if RUBY_VERSION.to_i < 2
19 | require 'jbb_layers_panel/z_win32api/Win32API.so'
20 | else
21 | require 'Win32API'
22 | end#if
23 |
24 | WS_CAPTION = 0x00C00000
25 | WS_EX_TOOLWINDOW = 0x00000080
26 |
27 | GWL_STYLE = -16
28 | GWL_EXSTYLE = -20
29 |
30 | # SetWindowPos() flags
31 | SWP_NOSIZE = 0x0001
32 | SWP_NOMOVE = 0x0002
33 | SWP_DRAWFRAME = 0x0020
34 | SWP_FRAMECHANGED = 0x0020
35 | SWP_NOREPOSITION = 0x0200
36 | WS_MAXIMIZEBOX = 0x10000
37 | WS_MINIMIZEBOX = 0x20000
38 | WS_SIZEBOX = 0x40000
39 |
40 | # Windows Functions
41 | #FindWindow = Win32API.new("user32.dll" , "FindWindow" , 'PP' , 'L')
42 | #FindWindowEx = Win32API.new("user32.dll", "FindWindowEx" , 'LLPP', 'L')
43 | SetWindowPos = Win32API.new("user32.dll" , "SetWindowPos" , 'LLIIIII', 'I')
44 | SetWindowLong = Win32API.new("user32.dll" , "SetWindowLong", 'LIL', 'L')
45 | GetWindowLong = Win32API.new("user32.dll" , "GetWindowLong", 'LI' , 'L')
46 | GetActiveWindow = Win32API.new("user32.dll", "GetActiveWindow", '', 'L')
47 | #GetForegroundWindow = Win32API.new("user32.dll", "GetForegroundWindow", '', 'L')
48 | GetWindowText = Win32API.new("user32.dll", "GetWindowText", 'LPI', 'I')
49 | GetWindowTextLength = Win32API.new("user32.dll", "GetWindowTextLength", 'L', 'I')
50 |
51 | end#if
52 |
53 | #-----------------------------------------------------------------------------
54 |
55 | #Sketchucation API
56 | @scfKey = 'JiBiBojabxz41322768h'
57 | begin
58 | SCFapi.store_event(@scfKey, 'Layers_Panel', 'Load')
59 | @scfApi = true
60 | rescue
61 | @scfApi = false
62 | end
63 |
64 | @isActive = true
65 |
66 | @model = Sketchup.active_model
67 | @layers = @model.layers
68 | @entityObservers = Hash.new
69 | @layerDictID = nil
70 | @layer_to_ID = []
71 | @dialog = nil
72 | @dialogStates = nil
73 | @allowSerialize = true
74 | @allowStatesChange = true
75 | @previousPageDict = nil
76 | @previousPageDict2 = nil
77 | @previousPageDict3 = nil
78 | @previousPageDict4 = nil
79 | @check = nil
80 | @selectedPageLayers = nil
81 | @timerCheckUpdate = nil
82 | @previousState = nil
83 | @heightBeforeMinimize = @heightBeforeMinimizeStates = 300
84 |
85 | @jbb_lp_pagesObserver = nil
86 | @jbb_lp_modelObserver = nil
87 | @jbb_lp_appObserver = nil
88 | @jbb_lp_entityObserver = nil
89 | @jbb_lp_layersObserver = nil
90 | @jbb_lp_viewObserver = nil
91 | @jbb_lp_renderingOptionsObserver = nil
92 |
93 | @lastActiveModelID = nil
94 |
95 | @html_path = File.dirname( __FILE__ ) + "/html/layers Panel.html"
96 | @html_path2 = File.dirname( __FILE__ ) + "/html/warning.html"
97 | @html_path3 = File.dirname( __FILE__ ) + "/html/options.html"
98 | @html_path4 = File.dirname( __FILE__ ) + "/html/debug.html"
99 | @html_path5 = File.dirname( __FILE__ ) + "/html/color.html"
100 | @html_path6 = File.dirname( __FILE__ ) + "/html/states.html"
101 | @html_path7 = File.dirname( __FILE__ ) + "/html/history.html"
102 |
103 | class << self
104 | attr_accessor :version, :scfKey, :scfApi, :isActive, :model, :layers, :entityObservers, :layerDictID, :dialog, :dialogStates, :allowSerialize, :allowStatesChange, :previousPageDict, :previousPageDict2, :previousPageDict3, :previousPageDict4, :check, :selectedPageLayers, :timerCheckUpdate, :previousState, :heightBeforeMinimize, :jbb_lp_pagesObserver, :jbb_lp_modelObserver, :jbb_lp_appObserver, :jbb_lp_entityObserver, :jbb_lp_layersObserver, :jbb_lp_viewObserver, :jbb_lp_renderingOptionsObserver, :lastActiveModelID
105 | end
106 |
107 |
108 |
109 | require 'jbb_layers_panel/rb/methods.rb'
110 |
111 | require 'jbb_layers_panel/rb/dialog.rb'
112 |
113 | require 'jbb_layers_panel/rb/observers.rb'
114 |
115 | require 'jbb_layers_panel/rb/menu_toolbar.rb'
116 |
117 | require 'jbb_layers_panel/rb/warning.rb'
118 |
119 | require 'jbb_layers_panel/rb/options.rb'
120 |
121 | require 'jbb_layers_panel/rb/debug.rb'
122 |
123 | require 'jbb_layers_panel/rb/history.rb'
124 |
125 | require 'jbb_layers_panel/rb/color.rb'
126 |
127 | require 'jbb_layers_panel/rb/states.rb'
128 |
129 |
130 |
131 | ### STARTUP TRIGGERS ### ------------------------------------------------------
132 |
133 | self.createDialog
134 |
135 | if WIN
136 | self.openedModel(Sketchup.active_model)
137 | end#if
138 |
139 | if Sketchup.read_default("jbb_layers_panel", "startup") == true
140 | self.showDialog(@dialog, true)
141 | self.make_toolwindow_frame("Layers Panel")
142 | self.createDialogStates
143 | end#if
144 |
145 | end#module
146 |
147 | #-----------------------------------------------------------------------------
148 | file_loaded( File.basename(__FILE__) )
149 |
--------------------------------------------------------------------------------
/jbb_layers_panel/html/Layers Panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
61 |
62 |
70 |
71 |
72 |
82 |
83 |
84 |
91 |
92 |
93 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | Layer0
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/jbb_layers_panel/rb/methods.rb:
--------------------------------------------------------------------------------
1 |
2 | module JBB_LayersPanel
3 |
4 |
5 |
6 | def self.set_attribute(target, name, key, value)
7 | if key && key != ""
8 | target.set_attribute(name, key, value)
9 | else
10 | puts "Attribute key is empty - Name : " + name + " - value : " + value.to_s
11 | end#if
12 | end#def
13 |
14 | def self.jsonToHash(string)
15 | string = string.to_s.gsub('/',"\\").gsub('\\\\',"\\") #This is for unicode values
16 | hashString = eval( string.inspect.gsub(':','=>') ) #Convert Json string to hash string
17 | hash = eval(hashString) #Convert hash string to proper hash object
18 | return hash
19 | end#def
20 |
21 | def self.showDialog(dialog, mainDialog = false)
22 | if OSX
23 | dialog.show_modal()
24 | if mainDialog
25 | width = Sketchup.read_default("jbb_layers_panel", "dialog_width")
26 | height = Sketchup.read_default("jbb_layers_panel", "dialog_height")
27 | width = 215 if width == nil
28 | height = 300 if height == nil
29 | dialog.set_size(width,height)
30 |
31 | x = Sketchup.read_default("jbb_layers_panel", "dialog_x")
32 | y = Sketchup.read_default("jbb_layers_panel", "dialog_y")
33 | x = 300 if x == nil
34 | y = 200 if y == nil
35 | dialog.set_position(x,y)
36 | end#if
37 | else
38 | dialog.show()
39 | end#if
40 | SCFapi.store_event(@scfKey, 'Layers_Panel', 'Dialog', 'Open') if @scfApi
41 | end#def
42 |
43 | def self.resetVariables
44 | @model = Sketchup.active_model
45 | @layers = @model.layers
46 | @layerDictID = @model.get_attribute("jbb_layerspanel", "layerDictID")
47 | end#def
48 |
49 | class WebdialogBridge < UI::WebDialog
50 | def add_bridge_callback(callback, &block)
51 | add_action_callback(callback) do |webdialog, params|
52 | if callback != "startup" && callback != "useRenderEngine" && callback != "getCollapsedGroups"
53 | JBB_LayersPanel.empty_layers_to_id_stack
54 | # puts callback
55 | end#if
56 | # puts "add_bridge_callback(#{callback}) { |#{params}| }"
57 | block.call(webdialog, params)
58 | execute_script('skpCallbackReceived();')
59 | end
60 | end
61 | end # WebdialogBridge
62 |
63 | def self.currentContext
64 | if @model.pages.selected_page == nil
65 | return @model
66 | else
67 | return @model.pages.selected_page
68 | end#if
69 | end#def
70 |
71 |
72 | ### LAYER SERIALIZE ### ------------------------------------------------------
73 |
74 | def self.incLayerDictID
75 | @layerDictID = @layerDictID + 1
76 | self.set_attribute(@model, "jbb_layerspanel", "layerDictID", @layerDictID) #Store incremented layerDictID in model attribute dict
77 | # puts "incLayerDictID"
78 | end#def
79 |
80 | def self.initializeLayerDictID
81 | if @layerDictID == nil
82 | if @model.get_attribute("jbb_layerspanel", "layerDictID") != nil #Get layerDictID from model if exists
83 | @layerDictID = @model.get_attribute("jbb_layerspanel", "layerDictID")
84 | else #Else, create it
85 | layer0 = @layers[0]
86 | layer0.set_attribute("jbb_layerspanel", "ID", 0) if !layer0.deleted? #Give Layer0 ID 0
87 | @layerDictID = 0
88 | end#if
89 | self.incLayerDictID
90 | end#if
91 | end#def
92 |
93 | def self.IDLayer(layer) #Give a unique custom id to a layer
94 | if layer.get_attribute("jbb_layerspanel", "ID") != nil
95 | #puts layer.name + " already IDed " + layer.get_attribute("jbb_layerspanel", "ID").to_s
96 | else
97 | self.incLayerDictID
98 | self.set_attribute(layer, "jbb_layerspanel", "ID", @layerDictID)
99 | # puts "layerDictID " + @layerDictID.to_s
100 | end#if
101 | end#def
102 |
103 | def self.empty_layers_to_id_stack
104 | @model.start_operation("Layers Panel ID", true)
105 | self.initializeLayerDictID
106 | @layer_to_ID.each{|layer|
107 | self.IDLayer(layer) if !layer.deleted?
108 | }
109 | @layer_to_ID = []
110 | @model.commit_operation
111 | end#def
112 |
113 | def self.checkEntityObserver(layer) #check if layer has observer, else attach to it
114 | if @entityObservers[layer.entityID] != true
115 | @entityObservers[layer.entityID] = true
116 | layer.add_observer(@jbb_lp_entityObserver)
117 | # puts layer.name
118 | end#if
119 | end#def
120 |
121 |
122 | ### LAYER DELETE METHOD ### ------------------------------------------------------
123 | #Simple modification of TIG's snippet delete-layer.rb
124 | #Basically, move or delete layer content, then creates an entry for every layer except one to delete, then purge layers, then delete entries
125 |
126 | def self.deleteLayer(layer, delete_geometry=false, currentLayer=false, operation=true)
127 | @model.start_operation("Delete layer", true) if operation
128 | @allowSerialize = false
129 | ents=@model.entities; defs=@model.definitions
130 | if delete_geometry
131 | allents=[]
132 | @model.entities.each{|e|allents< highestNumber
170 | }
171 | end#if
172 | highestNumber = highestNumber + 1
173 | groupName = 'Group ' + highestNumber.to_s
174 | end#if
175 |
176 | self.initializeLayerDictID
177 | self.incLayerDictID
178 | self.set_attribute(@model, "jbb_layerspanel_groups", @layerDictID, groupName) #Store group's name with ID
179 |
180 | serialized = @model.get_attribute("jbb_layerspanel", "serialized")
181 | serialized = "" if serialized == nil
182 | serialized = serialized + 'group[' + @layerDictID.to_s + ']=null'
183 | self.set_attribute(@model, "jbb_layerspanel", "serialized", serialized)
184 |
185 | self.refreshDialog
186 | @dialogStates.execute_script("visibilityChanged();") if @dialogStates != nil
187 | @previousState = 0
188 |
189 | return @layerDictID
190 | end#def
191 |
192 | def self.delete_group(groupID)
193 | state = false
194 | serialized = @model.get_attribute("jbb_layerspanel", "serialized").to_s
195 | serialized.gsub!(/group\[#{groupID}\]\=(\d+|null)/) { |match|
196 | state = true if match
197 | ''
198 | }
199 | #Get rid of extra "&" (At the start/end of the string, or when there's two of them)
200 | serialized.gsub!(/\A(&)/) { |match|
201 | ''
202 | }
203 | serialized.gsub!(/(&)\z/) { |match|
204 | ''
205 | }
206 | serialized.gsub!(/&{2}/) { |match|
207 | '&'
208 | }
209 | #Remove groupID as parent from other items
210 | serialized.gsub!(/(layer|group)\[\d+\]\=#{groupID}/) { |match|
211 | match.gsub!(/\=#{groupID}/) { |match| '=null' }
212 | }
213 | self.set_attribute(@model, "jbb_layerspanel", "serialized", serialized)
214 | self.refreshDialog
215 | return state
216 | end#def
217 |
218 | def self.rename_group(groupID, groupName)
219 | self.set_attribute(@model, "jbb_layerspanel_groups", groupID, groupName.to_s)
220 | self.refreshDialog
221 | return true
222 | end#def
223 |
224 | def self.collapse_group(groupID, all_scenes = true)
225 | self.set_attribute(@model, "jbb_layerspanel_collapseGroups", groupID, 1)
226 | if @model.pages.length > 0
227 | if all_scenes
228 | @model.pages.each{|page|
229 | self.set_attribute(page, "jbb_layerspanel_collapseGroups", groupID, 1)
230 | }
231 | else
232 | self.set_attribute(@model.pages.selected_page, "jbb_layerspanel_collapseGroups", groupID, 1)
233 | end#if
234 | end#if
235 | @dialogStates.execute_script("visibilityChanged();") if @dialogStates != nil
236 | @previousState = 0
237 | self.refreshDialog
238 | end#def
239 |
240 | def self.get_layer_by_ID(layerID)
241 | @layers.each{|layer|
242 | if layer.get_attribute("jbb_layerspanel", "ID").to_i == layerID.to_i #if layer's dict ID == match ID
243 | return layer
244 | break
245 | end#if
246 | }
247 | return nil
248 | end#def
249 |
250 | def self.get_layerID(layer)
251 | return layer.get_attribute("jbb_layerspanel", "ID").to_i
252 | end#def
253 |
254 | def self.get_groupID_by_name(groupName)
255 | ids = []
256 | if @model.attribute_dictionaries["jbb_layerspanel_groups"] != nil
257 | @model.attribute_dictionaries["jbb_layerspanel_groups"].each { | groupID, name |
258 | if groupName == name
259 | ids << groupID.to_i
260 | end#if
261 | }
262 | if ids.length > 1
263 | return ids
264 | else
265 | return ids[0]
266 | end#if
267 | end#if
268 | return nil
269 | end#def
270 |
271 | def self.get_group_name_by_ID(groupID)
272 | return @model.get_attribute("jbb_layerspanel_groups", groupID)
273 | end#def
274 |
275 | def self.nest_into(itemID, targetID)
276 | item = target = nil
277 | if itemID != targetID
278 | serialized = @model.get_attribute("jbb_layerspanel", "serialized") #retreive string of serialized layers
279 | target = (/(layer|group)\[#{targetID}\]\=(\d+|null)/).match(serialized) #Check that target exists
280 | if target
281 | serialized.to_s.gsub!(/(layer|group)\[#{itemID}\]\=(\d+|null)/) { |match|
282 | item = match
283 | match.gsub!(/\=(\d+|null)/) { |m| '=' + targetID.to_s } #Replace item parent by target
284 | }
285 | self.set_attribute(@model, "jbb_layerspanel", "serialized", serialized)
286 | self.move_nextTo(itemID, targetID, "after", false)
287 | self.refreshDialog
288 | end#if
289 | end#if
290 |
291 | if target && item && itemID != targetID
292 | return true
293 | else
294 | return false
295 | end#if
296 | end#def
297 |
298 | def self.move_before(itemID, targetID)
299 | self.move_nextTo(itemID, targetID, "before")
300 | end#def
301 |
302 | def self.move_after(itemID, targetID)
303 | self.move_nextTo(itemID, targetID, "after")
304 | end#def
305 |
306 | def self.move_nextTo(itemID, targetID, side, replaceParent = true)
307 | if itemID != targetID
308 | serialized = @model.get_attribute("jbb_layerspanel", "serialized") #retreive string of serialized layers
309 | item = target = parent = nil
310 | #Check that target exists
311 | target = (/(layer|group)\[#{targetID}\]\=(\d+|null)/).match(serialized)
312 | parent = target.captures[1] if target
313 | #Erase item from the serialized string
314 | if target
315 | serialized.to_s.gsub!(/(layer|group)\[#{itemID}\]\=(\d+|null)/) { |match|
316 | item = match
317 | item.gsub!(/\=(\d+|null)/) { |match| '=' + parent } if replaceParent #Replace item parent by target parent
318 | ''
319 | }
320 | end#if
321 | if item && item != target
322 | #Put item next to target
323 | serialized.to_s.gsub!(/(layer|group)\[#{targetID}\]\=(\d+|null)/) { |match|
324 | if side == "before"
325 | item + '&' + match
326 | elsif side == "after"
327 | match + '&' + item
328 | end#if
329 | }
330 | end#if
331 | #Get rid of extra "&" (At the start/end of the string, or when there's two of them)
332 | serialized.to_s.gsub!(/\A(&)/) { |match|
333 | ''
334 | }
335 | serialized.to_s.gsub!(/(&)\z/) { |match|
336 | ''
337 | }
338 | serialized.to_s.gsub!(/&{2}/) { |match|
339 | '&'
340 | }
341 | self.set_attribute(@model, "jbb_layerspanel", "serialized", serialized) #Store serialized in model attribute dict
342 | self.refreshDialog
343 | end#if
344 |
345 | if target && item && itemID != targetID
346 | return true
347 | else
348 | return false
349 | end#if
350 | end#def
351 |
352 | def self.render?(layer)
353 | layerID = layer.get_attribute("jbb_layerspanel", "ID")
354 | context = self.currentContext
355 | if context.get_attribute("jbb_layerspanel_render", layerID) == 0
356 | return false
357 | elsif context.get_attribute("jbb_layerspanel_render", layerID) == 1
358 | return false
359 | else
360 | return true
361 | end#if
362 | end#def
363 |
364 | def self.set_render_behav(layer, bool)
365 | layerID = layer.get_attribute("jbb_layerspanel", "ID")
366 | context = self.currentContext
367 | if bool == false
368 | self.set_attribute(context, "jbb_layerspanel_render", layerID, 0)
369 | else
370 | self.set_attribute(context, "jbb_layerspanel_render", layerID, 2)
371 | end#if
372 | self.refreshDialog
373 | return nil
374 | end#def
375 |
376 | end#module
--------------------------------------------------------------------------------
/jbb_layers_panel/css/layerspanel.css:
--------------------------------------------------------------------------------
1 |
2 | html {
3 | height: 100%;
4 | }
5 |
6 | body {
7 | margin: 0;
8 | height: 100%;
9 | width: 100%;
10 | color: #333;
11 | background-color: #c6c6c6;
12 | font-size: 12px;
13 | font-family: arial;
14 | -webkit-touch-callout: none;
15 | -webkit-user-select: none;
16 | -khtml-user-select: none;
17 | -moz-user-select: none;
18 | -ms-user-select: none;
19 | user-select: none;
20 | padding: 0 0 30px 0;
21 | overflow-x: hidden;
22 | overflow-y: hidden;
23 | margin: 57px 0 30px 0;
24 | }
25 |
26 |
27 |
28 | /****** HEADER ******/
29 |
30 | #minimize {
31 | width: 100%;
32 | height: 3px;
33 | position: fixed;
34 | top: 0;
35 | left: 0;
36 | background-color: #949494;
37 | cursor: pointer;
38 | }
39 |
40 | .left {
41 | float: left;
42 | }
43 |
44 | .right {
45 | float: right;
46 | }
47 |
48 | #header {
49 | background: #d6d6d6;
50 | width: 100%;
51 | position: fixed;
52 | top: 3px;
53 | z-index: 200;
54 | }
55 |
56 | .toolbar{
57 | width: 100%;
58 | height: 26px;
59 | border-bottom: 1px solid #9e9e9e;
60 | }
61 |
62 | #utilitiesBar{
63 | height: 24px;
64 | border-top: 1px solid #efefef;
65 | }
66 |
67 | #renderBar{
68 | border-top: 1px solid #efefef;
69 | height: 26px;
70 | }
71 |
72 | .headerElement {
73 | width: 26px;
74 | height: 100%;
75 | }
76 |
77 | .headerElementHover {
78 | background: url("./img/headerElement.png") no-repeat 0px 0px;
79 | }
80 |
81 | .headerElementClick{
82 | background: url("./img/headerElementBis.png") no-repeat 0px 0px;
83 | }
84 |
85 | .headerElement2 {
86 | width: 22px;
87 | margin-top: 1px;
88 | margin-right: 3px;
89 | height: 100%;
90 | }
91 |
92 | .headerElement2Hover {
93 | background: url("./img/headerElement2.png") no-repeat 0px 0px;
94 | }
95 |
96 | .headerElement2Click{
97 | background: url("./img/headerElement2bis.png") no-repeat 0px 0px;
98 | }
99 |
100 | /****** Render bar ******/
101 |
102 | #renderListButton{
103 | background: url("./img/listButton.png") no-repeat 0px -53px;
104 | width: 70px;
105 | height: 25px;
106 | float: left;
107 | padding-left: 8px;
108 | margin-top: 2px;
109 | }
110 |
111 | #renderListButton.clicked{
112 | background: url("./img/listButton.png") no-repeat 0px -79px;
113 | }
114 |
115 | #renderListButton span {
116 | background: url("./img/listButton.png") no-repeat right 0px;
117 | margin-top: -1px;
118 | display: block;
119 | overflow: hidden;
120 | white-space: nowrap;
121 | height: 25px;
122 | line-height: 25px;
123 | cursor: pointer;
124 | width: 100%;
125 | display: block; }
126 |
127 | #renderListButton.clicked span{
128 | background: url("./img/listButton.png") no-repeat right -26px;
129 | }
130 |
131 | #renderList {
132 | position: fixed;
133 | left: 1px;
134 | top: 33px;
135 | }
136 |
137 | .renderButton {
138 | width: 26px;
139 | height: 26px;
140 | }
141 |
142 | #vray {
143 | background: url("./img/vray.png") no-repeat 5px 4px;
144 | }
145 | #vrayrt {
146 | background: url("./img/vrayrt.png") no-repeat 5px 4px;
147 | }
148 |
149 | #mx {
150 | background: url("./img/maxwell_16.png") no-repeat 5px 5px;
151 | }
152 | #mxstudio {
153 | background: url("./img/studio_16.png") no-repeat 5px 5px;
154 | }
155 | #mxnet {
156 | background: url("./img/network_16.png") no-repeat 5px 5px;
157 | }
158 | #mxfire {
159 | background: url("./img/fire_16.png") no-repeat 5px 5px;
160 | }
161 |
162 | #kt{
163 | background: url("./img/kt_icon.png") no-repeat 1px 1px;
164 | }
165 |
166 | #ks{
167 | background: url("./img/keyshot_icon.png") no-repeat 5px 5px;
168 | }
169 |
170 | #indigo{
171 | background: url("./img/IndigoLogo.gif") no-repeat 1px 1px;
172 | }
173 |
174 | #podium{
175 | background: url("./img/render_16.png") no-repeat 5px 4px;
176 | }
177 |
178 | #menuButton {
179 | background: url("./img/menu2.png") no-repeat 4px 3px;
180 | width: 25px;
181 | height: 100%;
182 | }
183 |
184 | #historyButton {
185 | background: url("./img/history.png") no-repeat 3px 3px;
186 | width: 25px;
187 | height: 100%;
188 | }
189 |
190 | /****** Utilities bar ******/
191 |
192 | #current {
193 | background: url("./img/current.png") no-repeat 2px 3px;
194 | width: 25px;
195 | height: 100%;
196 | }
197 |
198 | #highlight {
199 | background: url("./img/highlight.png") no-repeat 2px 3px;
200 | width: 25px;
201 | height: 100%;
202 | }
203 |
204 | #select {
205 | background: url("./img/select.png") no-repeat 2px 3px;
206 | width: 25px;
207 | height: 100%;
208 | }
209 |
210 | #moveSel {
211 | background: url("./img/moveSel.png") no-repeat 2px 3px;
212 | width: 25px;
213 | height: 100%;
214 | }
215 |
216 | #lock1 {
217 | background: url("./img/lock.png") no-repeat 7px 3px;
218 | width: 25px;
219 | height: 100%;
220 | float: right;
221 | }
222 |
223 |
224 | /****** IE warning ******/
225 |
226 | #browser {
227 | width: 100%;
228 | background-color: #ff5a00;
229 | padding: 2px;
230 | padding-right: 30px;
231 | font-weight: bold;
232 | }
233 |
234 | #browser a {
235 | text-decoration: underline;
236 | }
237 |
238 |
239 | /******* MENU ******/
240 |
241 | .menu {
242 | display: none;
243 | background: #eee;
244 | border: 1px solid #999;
245 | z-index: 397;
246 | font-size: 11px;
247 | box-shadow: 3px 4px 5px #aaa;
248 | }
249 |
250 | .menuWrapper {
251 | background: #eee;
252 | border: 1px solid #999;
253 | width: 100%;
254 | height: 100%;
255 | position: absolute;
256 | bottom: 3px;
257 | left: -4px;
258 | z-index: 398;
259 | }
260 |
261 | #menu {
262 | width: 125px;
263 | position: fixed;
264 | top: 59px;
265 | left: 1px;
266 | }
267 |
268 | .menuElement {
269 | padding: 3px 5px 3px 5px;
270 | margin: 2px;
271 | border: 1px solid #eee;
272 | }
273 |
274 | .menuElement:hover {
275 | background: #ddd;
276 | border: 1px solid #999;
277 | }
278 |
279 | .seperator{
280 | margin-left: 3px;
281 | width: 93%;
282 | border-bottom: 1px solid #bbb;
283 | }
284 |
285 |
286 | /******* CONTEXT MENU ******/
287 |
288 | #contextmenu {
289 | width: 125px;
290 | position: fixed;
291 | top: 1px;
292 | left: 1px;
293 | }
294 |
295 | .disabledLink{
296 | cursor: default;
297 | color: #888;
298 | }
299 |
300 | .disabledLink > .menuElement:hover {
301 | background: #eee;
302 | border: 1px solid #ccc;
303 | }
304 |
305 |
306 | /****** LAYERS ******/
307 |
308 | #layersContainer {
309 | position: fixed;
310 | width: 200%;
311 | margin: -20px;
312 | }
313 |
314 | #layersContainer2 {
315 | height: 100%;
316 | width: 50%;
317 | margin: 20px;
318 | padding: 0 0 30px 0;
319 | overflow-y: scroll;
320 | }
321 |
322 | #layers {
323 | height: 100%;
324 | }
325 |
326 | a{
327 | color: #333;
328 | text-decoration: none;
329 | }
330 |
331 | .placeholder {
332 | outline: 1px dashed #4183C4;
333 | margin: 4px;
334 | height: 13px;
335 | }
336 |
337 | #helpers{
338 | list-style-type: none;
339 | position: absolute;
340 | }
341 |
342 | .group > ol{
343 | list-style-type: none;
344 | }
345 |
346 | .ui-sortable-helper{
347 | }
348 |
349 | .mjs-nestedSortable-error {
350 | background: #fbe3e4;
351 | border-color: transparent;
352 | }
353 |
354 | ol {
355 | margin: 0;
356 | padding: 0;
357 | padding-left: 12px;
358 | }
359 |
360 | ol.sortable, ol.sortable ol {
361 | margin-left: 12px;
362 | padding: 0;
363 | list-style-type: none;
364 | }
365 |
366 | ol.sortable {
367 | margin: 0 0;
368 | }
369 |
370 | .sortable li {
371 | padding: 0;
372 | }
373 |
374 | li.mjs-nestedSortable-collapsed.mjs-nestedSortable-hovering div {
375 | border-color: #999;
376 | background: #fafafa;
377 | }
378 |
379 | .sortable li.mjs-nestedSortable-collapsed > ol {
380 | display: none;
381 | }
382 |
383 | .sortable li.mjs-nestedSortable-branch > div > .disclose {
384 | display: inline-block;
385 | }
386 |
387 | .lidiv {
388 | height: 20px;
389 | background: #d6d6d6;
390 | position: relative;
391 | list-style: none;
392 | border-bottom: 1px solid #9e9e9e;
393 | border-top: 1px solid #9e9e9e;
394 | border-right: 1px solid #9e9e9e;
395 | margin-top: -1px;
396 | }
397 |
398 | .group > .lidiv {
399 | background: #ddd;
400 | }
401 |
402 | .ui-selecting , .group > .ui-selecting {
403 | background: #d3dbeb;
404 | }
405 |
406 | .ui-selected, .group > .ui-selected {
407 | background: #b7cdf9;
408 | }
409 |
410 | .ui-sortable-helper {
411 | border-top: 1px solid #9e9e9e;
412 | }
413 |
414 | .ui-sortable-placeholder {
415 | border-bottom: 1px solid #9e9e9e;
416 | }
417 |
418 | .rendering{
419 | padding: 0;
420 | float: right;
421 | height: 100%;
422 | width: 24px;
423 | border-left: 1px solid #9e9e9e;
424 | cursor: pointer;
425 | }
426 |
427 | .render{
428 | background: url("./img/render.png") no-repeat 3px 3px;
429 | }
430 |
431 | .noRender{
432 | background: url("./img/square.png") no-repeat 6px 4px;
433 | }
434 |
435 | .noRenderByGroup{
436 | background: url("./img/render2.png") no-repeat 3px 3px;
437 | }
438 |
439 | .visibility {
440 | padding: 0;
441 | float: right;
442 | height: 100%;
443 | width: 21px;
444 | border-left: 1px solid #9e9e9e;
445 | cursor: pointer;
446 | }
447 |
448 | .visible{
449 | background: url("./img/eye.png") no-repeat 5px 4px;
450 | }
451 |
452 | .hidden {
453 | background: url("./img/square.png") no-repeat 5px 4px;
454 | }
455 |
456 | .hiddenByGroup {
457 | background: url("./img/eye2.png") no-repeat 5px 4px;
458 | }
459 |
460 | .active {
461 | padding: 0;
462 | float: right;
463 | height: 100%;
464 | width: 21px;
465 | border-left: 1px solid #9e9e9e;
466 | cursor: pointer;
467 | }
468 |
469 | .lock2 {
470 | padding: 0;
471 | float: right;
472 | height: 100%;
473 | width: 17px;
474 | display: none;
475 | }
476 |
477 | .locked {
478 | display: block;
479 | background: url("./img/lock.png") no-repeat 0px;
480 | }
481 |
482 | .enabled {
483 | background: url("./img/radio2.png") no-repeat 6px 6px;
484 | }
485 |
486 | .disabled {
487 | background: url("./img/radio1.png") no-repeat 6px 6px;
488 | }
489 |
490 | .handle {
491 | padding: 0;
492 | background: url("./img/updown.png") no-repeat 3px 7px;
493 | float: left;
494 | height: 100%;
495 | width: 11px;
496 | border-left: 1px solid #9e9e9e;
497 | border-right: 1px solid #9e9e9e;
498 | cursor: move;
499 | }
500 |
501 | .layerName {
502 | display: block;
503 | padding: 3px 0 0 8px;
504 | height: 22px;
505 | overflow: hidden;
506 | white-space: nowrap;
507 | text-overflow: ellipsis;
508 | }
509 |
510 | .inputName {
511 | font-size:1px;
512 | display: none;
513 | overflow: hidden;
514 | padding: 0px 0 0 11px;
515 | height: 20px;
516 | }
517 |
518 | .inputNameInput {
519 | width: 100%;
520 | }
521 |
522 |
523 | /**** Layer0 ****/
524 |
525 | #layer_0 > .lidiv {
526 | background: url("./img/layer0.png");
527 | border-top: 1px solid #9e9e9e;
528 | }
529 |
530 | #layer_0 > .ui-selected {
531 | background: url("./img/layer0s.png");
532 | }
533 |
534 | .handle0 {
535 | padding: 0;
536 | background: none;
537 | float: left;
538 | height: 100%;
539 | width: 11px;
540 | border-left: 1px solid #9e9e9e;
541 | border-right: 1px solid #9e9e9e;
542 | }
543 |
544 |
545 |
546 | /****** DISCLOSE ******/
547 |
548 | .disclose {
549 | padding: 0;
550 | display: block;
551 | float: left;
552 | width: 35px;
553 | height: 100%;
554 | cursor: pointer;
555 | }
556 |
557 | .mjs-nestedSortable-leaf > .lidiv > .disclose{
558 | background: url("./img/group.png") no-repeat 5px 5px;
559 | }
560 |
561 | .mjs-nestedSortable-branch > .lidiv > .disclose{
562 | background: url("./img/group.png") no-repeat 5px 5px;
563 | }
564 |
565 | .mjs-nestedSortable-collapsed > .lidiv > .disclose{
566 | background: url("./img/group2.png") no-repeat 5px 5px;
567 | }
568 |
569 | .mjs-nestedSortable-expanded > .lidiv > .disclose{
570 | background: url("./img/group.png") no-repeat 5px 5px;
571 | }
572 |
573 |
574 | /******* MENU LAYER ******/
575 |
576 | #menuLayer {
577 | width: 160px;
578 | position: fixed;
579 | bottom: 22px;
580 | right: 4px;
581 | padding: 0 8px 8px 8px;
582 | }
583 |
584 | #menuLayer > div {
585 | margin: 8px 0 0 0;
586 | }
587 |
588 | .submitButton{
589 | margin-right: 5px;
590 | margin-left: 5px;
591 | }
592 |
593 |
594 |
595 | /****** FOOTER ******/
596 |
597 | #footer {
598 | background: #d6d6d6;
599 | border-top: 2px solid #9e9e9e;
600 | width: 100%;
601 | height: 18px;
602 | position: fixed;
603 | bottom: 0px;
604 | }
605 |
606 | #footer2{
607 | border-top: 1px solid #efefef;
608 | width: 100%;
609 | height: 17px;
610 | position: fixed;
611 | bottom: 0px;
612 | padding: 0 20px 0 0;
613 | }
614 |
615 |
616 | .footerElement {
617 | margin: 0 1px 0 1px;
618 | width: 25px;
619 | height: 100%;
620 | float: right;
621 | }
622 |
623 | .footerElementChecked {
624 | margin: 0;
625 | background: #bbb;
626 | border-left: 1px solid #9e9e9e;
627 | border-right: 1px solid #9e9e9e;
628 | }
629 |
630 |
631 | .footerElementLeft {
632 | float: left;
633 | }
634 |
635 |
636 | .footerElementRight {
637 | float: right;
638 | }
639 |
640 |
641 | .footerHover {
642 | margin: 0;
643 | background: #efefef;
644 | border-left: 1px solid #9e9e9e;
645 | border-right: 1px solid #9e9e9e;
646 | }
647 |
648 |
649 | .footerClick {
650 | background: #9c9c9c;
651 | }
652 |
653 | #colors {
654 | background: url("./img/colors.png") no-repeat 6px 1px;
655 | width: 25px;
656 | height: 100%;
657 | float: right;
658 | }
659 |
660 |
661 | #newLayer {
662 | background: url("./img/layer2.png") no-repeat 5px 0px;
663 | width: 25px;
664 | height: 100%;
665 | float: right;
666 | }
667 |
668 | #newGroup {
669 | background: url("./img/newgroup.png") no-repeat 6px 0px;
670 | width: 25px;
671 | height: 100%;
672 | float: right;
673 | }
674 |
675 | .trash {
676 | width: 25px;
677 | height: 100%;
678 | float: right;
679 | }
680 |
681 | #trash {
682 | background: url("./img/trash1.png") no-repeat 6px 0px;
683 | }
684 |
685 | #trash2 {
686 | background: url("./img/trash2.png") no-repeat 6px 0px;
687 | }
688 |
689 | #trash3 {
690 | background: url("./img/trash3.png") no-repeat 6px 0px;
691 | }
692 |
693 | ##update {
694 | height: 50px;
695 | overflow: hidden;
696 | }
697 |
698 |
699 | /******* OVERLAY + DIALOG ******/
700 |
701 | #confirmDelete{
702 | color: #555;
703 | display: none;
704 | position: absolute;
705 | padding-left: 10px;
706 | padding-top: 10px;
707 | z-index:100;
708 | }
709 |
710 | #wrapper{
711 | margin-right: 10px;
712 | width: 300px;
713 | background-color: #c6c6c6;
714 | padding: 1px 10px 10px 10px;
715 | border: 1px solid #555;
716 | }
717 |
718 | #wrapper2 {
719 | z-index:105;
720 | }
721 |
722 | #wrapperButtons {
723 | text-align: center;
724 | }
725 |
726 | .deleteButton {
727 | margin: 20px 5px 0 5px;
728 | width: 50px;
729 | background-color:#eee;
730 | border:1px solid #888;
731 | display:inline-block;
732 | color:#777;
733 | font-family:arial;
734 | font-size:11px;
735 | font-weight:bold;
736 | padding:4px 9px;
737 | text-decoration:none;
738 | }
739 |
740 | .deleteButton:hover {
741 | background-color:#ddd;
742 | }
743 |
744 | .deleteButton:active {
745 | position:relative;
746 | top:1px;
747 | }
748 |
749 | #overlay {
750 | display: none;
751 | position: absolute;
752 | width: 100%;
753 | height: 100%;
754 | background-color: #000;
755 | z-index: 99;
756 | opacity: 0.5;
757 | -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";
758 | filter: alpha(opacity=40);
759 | }
--------------------------------------------------------------------------------
/jbb_layers_panel/rb/observers.rb:
--------------------------------------------------------------------------------
1 |
2 | module JBB_LayersPanel
3 |
4 | ### ENTITYOBSERVER ### ------------------------------------------------------
5 | #Watches for layers to be hidden or renamed (Which layers observer doesn't support)
6 |
7 | class JBB_LP_EntityObserver < Sketchup::EntityObserver
8 | @lastVisibleLayers = []
9 |
10 | def onChangeEntity(layer)
11 | if layer.deleted? == false
12 | # puts 'onchangeentity ' + layer.name
13 | if layer.get_attribute("jbb_layerspanel", "ID") != nil #Verify entity exists (onChangeEntity mistrigger)
14 | # puts layer.name
15 | if layer == JBB_LayersPanel.layers[0]
16 | layerID = 0
17 | else
18 | layerID = layer.get_attribute("jbb_layerspanel", "ID")
19 | end#if
20 |
21 | if layer.visible?
22 | showLayerFromRuby = "showLayerFromRuby('#{layerID}');"
23 | JBB_LayersPanel.dialog.execute_script(showLayerFromRuby)
24 | done_04b = false
25 | timer_04b = UI.start_timer(0, false) {
26 | next if done_04b
27 | done_04b = true
28 | if JBB_LayersPanel.allowSerialize == true
29 | JBB_LayersPanel.model.start_operation("Unhide layer", true, false, true)
30 | JBB_LayersPanel.unHideByGroup(layerID)
31 | JBB_LayersPanel.model.commit_operation
32 | end#if
33 | }
34 | else
35 | hideLayerFromRuby = "hideLayerFromRuby('#{layerID}');"
36 | JBB_LayersPanel.dialog.execute_script(hideLayerFromRuby)
37 | end#if
38 |
39 | renameLayerFromRuby = "renameLayerFromRuby('#{layerID}', '#{layer.name}');"
40 | JBB_LayersPanel.dialog.execute_script(renameLayerFromRuby)
41 |
42 | if Sketchup.read_default("jbb_layers_panel", "auto_update") == true
43 | done_04 = false
44 | timer_04 = UI.start_timer(0, false) {
45 | next if done_04
46 | done_04 = true
47 | if JBB_LayersPanel.model.pages.selected_page != nil
48 | JBB_LayersPanel.model.pages.selected_page.update(32) #Update page's layers state
49 | # puts "update " + JBB_LayersPanel.model.pages.selected_page.name
50 | end#if
51 | }
52 | end#if
53 |
54 | if JBB_LayersPanel.allowStatesChange
55 | visibleLayers = []
56 | JBB_LayersPanel.layers.each{|layer|
57 | visibleLayers << layer if layer.visible?
58 | }
59 | if @lastVisibleLayers != visibleLayers
60 | JBB_LayersPanel.dialogStates.execute_script("visibilityChanged();") if JBB_LayersPanel.dialogStates != nil
61 | JBB_LayersPanel.previousState = 0
62 | end#if
63 | @lastVisibleLayers = visibleLayers
64 | end#if
65 | end#if
66 | end#if
67 | end#def
68 | end#class
69 |
70 | @jbb_lp_entityObserver = JBB_LP_EntityObserver.new
71 |
72 | if OSX
73 | # Attach the observer to layer0
74 | @layers[0].add_observer(@jbb_lp_entityObserver)
75 | end#if
76 |
77 |
78 |
79 | ### LAYERSOBSERVER ### ------------------------------------------------------
80 |
81 | # Layers observer
82 | class JBB_LP_layersObserver < Sketchup::LayersObserver
83 |
84 | def onLayerAdded(layers, layer)
85 | done_02 = false
86 | timer_02 = UI.start_timer(0, false) {
87 | next if done_02
88 | done_02 = true
89 | if JBB_LayersPanel.allowSerialize == true
90 | if Sketchup.active_model.tools.active_tool_name != 'PasteTool'
91 | JBB_LayersPanel.model.start_operation("Add layer", true, true, true)
92 | end#if
93 | JBB_LayersPanel.layers.each {| l | layer = l }
94 | JBB_LayersPanel.initializeLayerDictID
95 | JBB_LayersPanel.IDLayer(layer)
96 | if JBB_LayersPanel.dialog
97 | layerIDForJS = layer.get_attribute("jbb_layerspanel", "ID")
98 | addLayerFromRuby = "addLayerFromRuby('#{layer.name}', '#{layerIDForJS}');"
99 | JBB_LayersPanel.dialog.execute_script(addLayerFromRuby)
100 | showLayerFromRuby = "showLayerFromRuby('#{layerIDForJS}');"
101 | JBB_LayersPanel.dialog.execute_script(showLayerFromRuby)
102 | if RUBY_VERSION.to_i >= 2
103 | JBB_LayersPanel.setColorFromRuby(layer)
104 | end#if
105 | end#if
106 | JBB_LayersPanel.checkEntityObserver(layer)
107 | JBB_LayersPanel.storeSerialize("Add layer")
108 | if Sketchup.active_model.tools.active_tool_name != 'PasteTool'
109 | JBB_LayersPanel.model.commit_operation
110 | end#if
111 | end#if
112 | if layer.name == "Google Earth Snapshot"
113 | UI.start_timer(0, false) {
114 | JBB_LayersPanel.dialog.execute_script("reloadDialog();")
115 | }
116 | end#if
117 | }
118 | end#onLayerAdded
119 |
120 | def onLayerRemoved(layers, layer)
121 | layerID = layer.get_attribute("jbb_layerspanel", "ID")
122 | deleteLayerFromRuby = "deleteLayerFromRuby('#{layerID}');"
123 | JBB_LayersPanel.dialog.execute_script(deleteLayerFromRuby)
124 | end#onLayerRemoved
125 |
126 |
127 | def onCurrentLayerChanged(layers, layer)
128 | if Sketchup.read_default("jbb_layers_panel", "display_warning") != false
129 | tool_name = Sketchup.active_model.tools.active_tool_name
130 | if layer != layers[0]
131 | if tool_name == "SketchTool" || tool_name == "RectangleTool" || tool_name == "CircleTool" || tool_name == "ArcTool" || tool_name == "PolyTool" || tool_name == "FreehandTool"
132 | JBB_LayersPanel.show_layerspanel_dlg_warning
133 | end#if
134 | else
135 | JBB_LayersPanel.close_layerspanel_dlg_warning
136 | end#if
137 | end#if
138 | if JBB_LayersPanel.dialog
139 | JBB_LayersPanel.dialog.execute_script('getActiveLayer();')
140 | end#if
141 | end#onLayerRemoved
142 |
143 | end#JBB_LP_layersObserver
144 |
145 | @jbb_lp_layersObserver = JBB_LP_layersObserver.new
146 |
147 | if OSX
148 | # Attach the observer.
149 | @layers.add_observer(@jbb_lp_layersObserver)
150 | end#if
151 |
152 |
153 |
154 | ### MODELOBSERVER ### ------------------------------------------------------
155 |
156 | class JBB_LP_ModelObserver < Sketchup::ModelObserver
157 | def onTransactionUndo(model)
158 | # puts "undo"
159 | JBB_LayersPanel.refreshDialog
160 | JBB_LayersPanel.refreshStatesDialog if JBB_LayersPanel.dialogStates != nil
161 | end#def
162 | def onTransactionRedo(model)
163 | # puts "redo"
164 | JBB_LayersPanel.refreshDialog
165 | JBB_LayersPanel.refreshStatesDialog if JBB_LayersPanel.dialogStates != nil
166 | end#def
167 | end#class
168 |
169 | @jbb_lp_modelObserver = JBB_LP_ModelObserver.new
170 |
171 | if OSX
172 | # Attach the observer
173 | @model.add_observer(@jbb_lp_modelObserver)
174 | end#if
175 |
176 |
177 |
178 | ### PAGESOBSERVER ### ------------------------------------------------------
179 |
180 | class JBB_LP_PagesObserver < Sketchup::PagesObserver
181 | def onContentsModified(pages)
182 | activePage = JBB_LayersPanel.model.pages.selected_page
183 |
184 | if JBB_LayersPanel.check == 0 #First trigger
185 | JBB_LayersPanel.checkPageUpdate
186 | JBB_LayersPanel.check = 1
187 |
188 | else #second trigger
189 | JBB_LayersPanel.previousPageDict = activePage.attribute_dictionary "jbb_layerspanel_collapseGroups", true
190 | JBB_LayersPanel.previousPageDict2 = activePage.attribute_dictionary "jbb_layerspanel_tempHiddenGroups", true
191 | JBB_LayersPanel.previousPageDict3 = activePage.attribute_dictionary "jbb_layerspanel_tempHiddenByGroupLayers", true
192 | JBB_LayersPanel.previousPageDict4 = activePage.attribute_dictionary "jbb_layerspanel_render", true
193 |
194 | dict = activePage.attribute_dictionary "jbb_layerspanel_hiddenGroups", true
195 | dict2 = activePage.attribute_dictionary "jbb_layerspanel_hiddenByGroupLayers", true
196 |
197 | JBB_LayersPanel.check = 0
198 |
199 | done_07 = false
200 | timer_07 = UI.start_timer(0, false) {
201 | next if done_07
202 | done_07 = true
203 | dict.each { | key, value |
204 | JBB_LayersPanel.set_attribute(activePage, "jbb_layerspanel_tempHiddenGroups", key, value)
205 | }
206 | dict2.each { | key, value |
207 | JBB_LayersPanel.set_attribute(activePage, "jbb_layerspanel_tempHiddenByGroupLayers", key, value)
208 | }
209 | JBB_LayersPanel.selectedPageLayers = activePage.layers
210 |
211 | JBB_LayersPanel.dialog.execute_script("emptyOl();")
212 | JBB_LayersPanel.getModelLayers(false)
213 | JBB_LayersPanel.getLayerColors()
214 | JBB_LayersPanel.getActiveLayer()
215 | JBB_LayersPanel.getCollapsedGroups()
216 | }
217 | end#if
218 | end#def
219 | def onElementAdded(pages, page)
220 | if JBB_LayersPanel.previousPageDict == nil
221 | dict = JBB_LayersPanel.model.attribute_dictionary "jbb_layerspanel_collapseGroups", true
222 | else
223 | dict = JBB_LayersPanel.previousPageDict
224 | end#if
225 |
226 | if JBB_LayersPanel.previousPageDict2 == nil
227 | dict2 = JBB_LayersPanel.model.attribute_dictionary "jbb_layerspanel_hiddenGroups", true
228 | else
229 | dict2 = JBB_LayersPanel.previousPageDict2
230 | end#if
231 |
232 | if JBB_LayersPanel.previousPageDict3 == nil
233 | dict3 = JBB_LayersPanel.model.attribute_dictionary "jbb_layerspanel_hiddenByGroupLayers", true
234 | else
235 | dict3 = JBB_LayersPanel.previousPageDict3
236 | end#if
237 |
238 | if JBB_LayersPanel.previousPageDict4 == nil
239 | dict4 = JBB_LayersPanel.model.attribute_dictionary "jbb_layerspanel_render", true
240 | else
241 | dict4 = JBB_LayersPanel.previousPageDict4
242 | end#if
243 |
244 | # puts "added " + page.name
245 | JBB_LayersPanel.check = 1
246 |
247 | done_08 = false
248 | timer_08 = UI.start_timer(0, false) {
249 | next if done_08
250 | done_08 = true
251 | dict.each { | key, value |
252 | JBB_LayersPanel.set_attribute(page, "jbb_layerspanel_collapseGroups", key, value)
253 | }
254 | dict2.each { | key, value |
255 | JBB_LayersPanel.set_attribute(page, "jbb_layerspanel_hiddenGroups", key, value)
256 | }
257 | dict3.each { | key, value |
258 | JBB_LayersPanel.set_attribute(page, "jbb_layerspanel_hiddenByGroupLayers", key, value)
259 | }
260 | dict4.each { | key, value |
261 | JBB_LayersPanel.set_attribute(page, "jbb_layerspanel_render", key, value)
262 | }
263 | activePage = JBB_LayersPanel.model.pages.selected_page
264 | JBB_LayersPanel.model.pages.selected_page = activePage
265 | }
266 | end#def
267 | end#class
268 |
269 | def self.checkPageUpdate
270 | if Sketchup.read_default("jbb_layers_panel", "auto_update") == false
271 | activePage = Sketchup.active_model.pages.selected_page
272 | begin
273 | if @selectedPageLayers == activePage.layers
274 | # puts "Not updated"
275 | else
276 | # puts "Updated !"
277 | self.updateDictionaries(activePage)
278 | end#if
279 | rescue
280 | end
281 |
282 | begin
283 | @selectedPageLayers = activePage.layers
284 | rescue
285 | end
286 | end#if
287 | end#def
288 |
289 | def self.updateDictionaries(activePage)
290 | done_09 = false
291 | timer_09 = UI.start_timer(0, false) {
292 | next if done_09
293 | done_09 = true
294 | dict = activePage.attribute_dictionary "jbb_layerspanel_tempHiddenGroups", true
295 | dict2 = activePage.attribute_dictionary "jbb_layerspanel_tempHiddenByGroupLayers", true
296 |
297 | dict.each { | key, value |
298 | JBB_LayersPanel.set_attribute(activePage, "jbb_layerspanel_hiddenGroups", key, value)
299 | }
300 | dict2.each { | key, value |
301 | JBB_LayersPanel.set_attribute(activePage, "jbb_layerspanel_hiddenByGroupLayers", key, value)
302 | }
303 | }
304 | end#def
305 |
306 | # Update page layers
307 | def self.startUpdateTimer
308 | begin
309 | @selectedPageLayers = Sketchup.active_model.pages.selected_page.layers
310 | rescue
311 | end
312 |
313 | @timerCheckUpdate = UI.start_timer(0.3, true) {
314 | if @check == 0
315 | self.checkPageUpdate
316 | end#if
317 | }
318 | end#def
319 | def self.stopUpdateTimer
320 | UI.stop_timer(@timerCheckUpdate)
321 | end#def
322 |
323 | if Sketchup.read_default("jbb_layers_panel", "auto_update") == false
324 | self.startUpdateTimer
325 | end#if
326 |
327 | @jbb_lp_pagesObserver = JBB_LP_PagesObserver.new
328 |
329 | if OSX
330 | # Attach the observer
331 | @model.pages.add_observer(@jbb_lp_pagesObserver)
332 | end#if
333 |
334 |
335 |
336 | ### RENDERINGOPTIONSOBSERVER ### ------------------------------------------------------
337 |
338 | #Track active model change
339 | class JBB_LP_RenderingOptionsObserver < Sketchup::RenderingOptionsObserver
340 | def onRenderingOptionsChanged(renderoptions, type)
341 | if type == 16
342 | if JBB_LayersPanel.model.rendering_options["DisplayColorByLayer"] == true
343 | JBB_LayersPanel.dialog.execute_script("toogleColorsButton(true);")
344 | else
345 | JBB_LayersPanel.dialog.execute_script("toogleColorsButton(false);")
346 | end#if
347 | end#if
348 | end
349 | end#def
350 |
351 | @jbb_lp_renderingOptionsObserver = JBB_LP_RenderingOptionsObserver.new
352 |
353 | if OSX
354 | # Attach the observer
355 | @model.rendering_options.add_observer(@jbb_lp_renderingOptionsObserver)
356 | end#if
357 |
358 |
359 |
360 | ### VIEWOBSERVER ### ------------------------------------------------------
361 |
362 | #Track active model change
363 | class JBB_LP_ViewObserver < Sketchup::ViewObserver
364 | def onViewChanged(view)
365 | if OSX
366 | # puts Sketchup.active_model.definitions.entityID
367 | if JBB_LayersPanel.lastActiveModelID != Sketchup.active_model.definitions.entityID
368 | JBB_LayersPanel.resetVariables
369 | JBB_LayersPanel.dialogStartup #Reload main dialog
370 | JBB_LayersPanel.refreshStatesDialog if JBB_LayersPanel.dialogStates != nil #Reload states dialog
371 | end#if
372 | JBB_LayersPanel.lastActiveModelID = Sketchup.active_model.definitions.entityID
373 | end#if
374 | end
375 | end#def
376 |
377 | if OSX
378 | @jbb_lp_viewObserver = JBB_LP_ViewObserver.new
379 |
380 | # Attach the observer
381 | @model.active_view.add_observer(@jbb_lp_viewObserver)
382 | end#if
383 |
384 |
385 |
386 | ### APPOBSERVER ### ------------------------------------------------------
387 |
388 | class JBB_LP_AppObserver < Sketchup::AppObserver
389 |
390 | def onNewModel(newModel)
391 | done_05 = false
392 | timer_05 = UI.start_timer(0, false) {
393 | next if done_05
394 | done_05 = true
395 | JBB_LayersPanel.openedModel(newModel)
396 | }
397 | end#def
398 |
399 | def onOpenModel(newModel)
400 | done_06 = false
401 | timer_06 = UI.start_timer(0, false) {
402 | next if done_06
403 | done_06 = true
404 | JBB_LayersPanel.openedModel(newModel)
405 | }
406 | end#def
407 |
408 | def onQuit()
409 | if OSX
410 | JBB_LayersPanel.storeSizeAndPosition
411 | end#if
412 | end#def
413 |
414 | end#class
415 |
416 | def self.openedModel(newModel)
417 | self.createDialog
418 | JBB_LayersPanel.model = newModel
419 | JBB_LayersPanel.layers = newModel.layers
420 |
421 | JBB_LayersPanel.layerDictID = nil
422 |
423 | JBB_LayersPanel.model.add_observer(JBB_LayersPanel.jbb_lp_modelObserver)
424 | JBB_LayersPanel.model.pages.add_observer(JBB_LayersPanel.jbb_lp_pagesObserver)
425 | JBB_LayersPanel.layers.add_observer(JBB_LayersPanel.jbb_lp_layersObserver)
426 | @model.rendering_options.add_observer(@jbb_lp_renderingOptionsObserver)
427 |
428 | if OSX #Track active model change
429 | JBB_LayersPanel.model.active_view.add_observer(JBB_LayersPanel.jbb_lp_viewObserver)
430 | end#if
431 |
432 | JBB_LayersPanel.layers.each{|layer|
433 | JBB_LayersPanel.checkEntityObserver(layer)
434 | }
435 |
436 | JBB_LayersPanel.dialog.execute_script("reloadDialog();")
437 | end#def
438 |
439 | @jbb_lp_appObserver = JBB_LP_AppObserver.new
440 |
441 | # Attach the observer
442 | Sketchup.add_observer(@jbb_lp_appObserver)
443 |
444 |
445 | end#module
--------------------------------------------------------------------------------
/jbb_layers_panel/js/jquery.mjs.nestedSortable.old.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery UI Nested Sortable
3 | * v 1.3.5 / 21 jun 2012
4 | * http://mjsarfatti.com/code/nestedSortable
5 | *
6 | * Depends on:
7 | * jquery.ui.sortable.js 1.8+
8 | *
9 | * Copyright (c) 2010-2012 Manuele J Sarfatti
10 | * Licensed under the MIT License
11 | * http://www.opensource.org/licenses/mit-license.php
12 | */
13 |
14 | (function($) {
15 |
16 | $.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
17 |
18 | options: {
19 | tabSize: 20,
20 | disableNesting: 'mjs-nestedSortable-no-nesting',
21 | errorClass: 'mjs-nestedSortable-error',
22 | doNotClear: false,
23 | listType: 'ol',
24 | maxLevels: 0,
25 | protectRoot: false,
26 | rootID: null,
27 | rtl: false,
28 | isAllowed: function(item, parent) { return true; }
29 | },
30 |
31 | _create: function() {
32 | this.element.data('sortable', this.element.data('nestedSortable'));
33 |
34 | if (!this.element.is(this.options.listType))
35 | throw new Error('nestedSortable: Please check the listType option is set to your actual list type');
36 |
37 | return $.ui.sortable.prototype._create.apply(this, arguments);
38 | },
39 |
40 | destroy: function() {
41 | this.element
42 | .removeData("nestedSortable")
43 | .unbind(".nestedSortable");
44 | return $.ui.sortable.prototype.destroy.apply(this, arguments);
45 | },
46 |
47 | _mouseDrag: function(event) {
48 |
49 | //Compute the helpers position
50 | this.position = this._generatePosition(event);
51 | this.positionAbs = this._convertPositionTo("absolute");
52 |
53 | if (!this.lastPositionAbs) {
54 | this.lastPositionAbs = this.positionAbs;
55 | }
56 |
57 | var o = this.options;
58 |
59 | //Do scrolling
60 | if(this.options.scroll) {
61 | var scrolled = false;
62 | if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
63 |
64 | if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
65 | this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
66 | else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
67 | this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
68 |
69 | if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
70 | this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
71 | else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
72 | this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
73 |
74 | } else {
75 |
76 | if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
77 | scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
78 | else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
79 | scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
80 |
81 | if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
82 | scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
83 | else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
84 | scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
85 |
86 | }
87 |
88 | if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
89 | $.ui.ddmanager.prepareOffsets(this, event);
90 | }
91 |
92 | //Regenerate the absolute position used for position checks
93 | this.positionAbs = this._convertPositionTo("absolute");
94 |
95 | // Find the top offset before rearrangement,
96 | var previousTopOffset = this.placeholder.offset().top;
97 |
98 | //Set the helper position
99 | if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
100 | if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
101 |
102 | //Rearrange
103 | for (var i = this.items.length - 1; i >= 0; i--) {
104 |
105 | //Cache variables and intersection, continue if no intersection
106 | var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
107 | if (!intersection) continue;
108 |
109 | if(itemElement != this.currentItem[0] //cannot intersect with itself
110 | && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
111 | && !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
112 | && (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
113 | //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
114 | ) {
115 |
116 | $(itemElement).mouseenter();
117 |
118 | this.direction = intersection == 1 ? "down" : "up";
119 |
120 | if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
121 | $(itemElement).mouseleave();
122 | this._rearrange(event, item);
123 | } else {
124 | break;
125 | }
126 |
127 | // Clear emtpy ul's/ol's
128 | this._clearEmpty(itemElement);
129 |
130 | this._trigger("change", event, this._uiHash());
131 | break;
132 | }
133 | }
134 |
135 | var parentItem = (this.placeholder[0].parentNode.parentNode &&
136 | $(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
137 | ? $(this.placeholder[0].parentNode.parentNode)
138 | : null,
139 | level = this._getLevel(this.placeholder),
140 | childLevels = this._getChildLevels(this.helper);
141 |
142 | // To find the previous sibling in the list, keep backtracking until we hit a valid list item.
143 | var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
144 | if (previousItem != null) {
145 | while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0] == this.currentItem[0] || previousItem[0] == this.helper[0]) {
146 | if (previousItem[0].previousSibling) {
147 | previousItem = $(previousItem[0].previousSibling);
148 | } else {
149 | previousItem = null;
150 | break;
151 | }
152 | }
153 | }
154 |
155 | // To find the next sibling in the list, keep stepping forward until we hit a valid list item.
156 | var nextItem = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null;
157 | if (nextItem != null) {
158 | while (nextItem[0].nodeName.toLowerCase() != 'li' || nextItem[0] == this.currentItem[0] || nextItem[0] == this.helper[0]) {
159 | if (nextItem[0].nextSibling) {
160 | nextItem = $(nextItem[0].nextSibling);
161 | } else {
162 | nextItem = null;
163 | break;
164 | }
165 | }
166 | }
167 |
168 | var newList = document.createElement(o.listType);
169 |
170 | this.beyondMaxLevels = 0;
171 |
172 | // If the item is moved to the left, send it to its parent's level unless there are siblings below it.
173 | if (parentItem != null && nextItem == null &&
174 | (o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth()) ||
175 | !o.rtl && (this.positionAbs.left < parentItem.offset().left))) {
176 | parentItem.after(this.placeholder[0]);
177 | this._clearEmpty(parentItem[0]);
178 | this._trigger("change", event, this._uiHash());
179 | }
180 | // If the item is below a sibling and is moved to the right, make it a child of that sibling.
181 | else if (previousItem != null &&
182 | (o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize) ||
183 | !o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize))) {
184 | this._isAllowed(previousItem, level, level+childLevels+1);
185 | if (!previousItem.children(o.listType).length) {
186 | previousItem[0].appendChild(newList);
187 | }
188 | // If this item is being moved from the top, add it to the top of the list.
189 | if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) {
190 | previousItem.children(o.listType).prepend(this.placeholder);
191 | }
192 | // Otherwise, add it to the bottom of the list.
193 | else {
194 | previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
195 | }
196 | this._trigger("change", event, this._uiHash());
197 | }
198 | else {
199 | this._isAllowed(parentItem, level, level+childLevels);
200 | }
201 |
202 | //Post events to containers
203 | this._contactContainers(event);
204 |
205 | //Interconnect with droppables
206 | if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
207 |
208 | //Call callbacks
209 | this._trigger('sort', event, this._uiHash());
210 |
211 | this.lastPositionAbs = this.positionAbs;
212 | return false;
213 |
214 | },
215 |
216 | _mouseStop: function(event, noPropagation) {
217 |
218 | // If the item is in a position not allowed, send it back
219 | if (this.beyondMaxLevels) {
220 |
221 | this.placeholder.removeClass(this.options.errorClass);
222 |
223 | if (this.domPosition.prev) {
224 | $(this.domPosition.prev).after(this.placeholder);
225 | } else {
226 | $(this.domPosition.parent).prepend(this.placeholder);
227 | }
228 |
229 | this._trigger("revert", event, this._uiHash());
230 |
231 | }
232 |
233 | // Clean last empty ul/ol
234 | for (var i = this.items.length - 1; i >= 0; i--) {
235 | var item = this.items[i].item[0];
236 | this._clearEmpty(item);
237 | }
238 |
239 | $.ui.sortable.prototype._mouseStop.apply(this, arguments);
240 |
241 | },
242 |
243 | serialize: function(options) {
244 |
245 | var o = $.extend({}, this.options, options),
246 | items = this._getItemsAsjQuery(o && o.connected),
247 | str = [];
248 |
249 | $(items).each(function() {
250 | var res = ($(o.item || this).attr(o.attribute || 'id') || '')
251 | .match(o.expression || (/(.+)[-=_](.+)/)),
252 | pid = ($(o.item || this).parent(o.listType)
253 | .parent(o.items)
254 | .attr(o.attribute || 'id') || '')
255 | .match(o.expression || (/(.+)[-=_](.+)/));
256 |
257 | if (res) {
258 | str.push(((o.key || res[1]) + '[' + (o.key && o.expression ? res[1] : res[2]) + ']')
259 | + '='
260 | + (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID));
261 | }
262 | });
263 |
264 | if(!str.length && o.key) {
265 | str.push(o.key + '=');
266 | }
267 |
268 | return str.join('&');
269 |
270 | },
271 |
272 | toHierarchy: function(options) {
273 |
274 | var o = $.extend({}, this.options, options),
275 | sDepth = o.startDepthCount || 0,
276 | ret = [];
277 |
278 | $(this.element).children(o.items).each(function () {
279 | var level = _recursiveItems(this);
280 | ret.push(level);
281 | });
282 |
283 | return ret;
284 |
285 | function _recursiveItems(item) {
286 | var id = ($(item).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
287 | if (id) {
288 | var currentItem = {"id" : id[2]};
289 | if ($(item).children(o.listType).children(o.items).length > 0) {
290 | currentItem.children = [];
291 | $(item).children(o.listType).children(o.items).each(function() {
292 | var level = _recursiveItems(this);
293 | currentItem.children.push(level);
294 | });
295 | }
296 | return currentItem;
297 | }
298 | }
299 | },
300 |
301 | toArray: function(options) {
302 |
303 | var o = $.extend({}, this.options, options),
304 | sDepth = o.startDepthCount || 0,
305 | ret = [],
306 | left = 2;
307 |
308 | ret.push({
309 | "item_id": o.rootID,
310 | "parent_id": 'none',
311 | "depth": sDepth,
312 | "left": '1',
313 | "right": ($(o.items, this.element).length + 1) * 2
314 | });
315 |
316 | $(this.element).children(o.items).each(function () {
317 | left = _recursiveArray(this, sDepth + 1, left);
318 | });
319 |
320 | ret = ret.sort(function(a,b){ return (a.left - b.left); });
321 |
322 | return ret;
323 |
324 | function _recursiveArray(item, depth, left) {
325 |
326 | var right = left + 1,
327 | id,
328 | pid;
329 |
330 | if ($(item).children(o.listType).children(o.items).length > 0) {
331 | depth ++;
332 | $(item).children(o.listType).children(o.items).each(function () {
333 | right = _recursiveArray($(this), depth, right);
334 | });
335 | depth --;
336 | }
337 |
338 | id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));
339 |
340 | if (depth === sDepth + 1) {
341 | pid = o.rootID;
342 | } else {
343 | var parentItem = ($(item).parent(o.listType)
344 | .parent(o.items)
345 | .attr(o.attribute || 'id'))
346 | .match(o.expression || (/(.+)[-=_](.+)/));
347 | pid = parentItem[2];
348 | }
349 |
350 | if (id) {
351 | ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
352 | }
353 |
354 | left = right + 1;
355 | return left;
356 | }
357 |
358 | },
359 |
360 | _clearEmpty: function(item) {
361 |
362 | var emptyList = $(item).children(this.options.listType);
363 | if (emptyList.length && !emptyList.children().length && !this.options.doNotClear) {
364 | emptyList.remove();
365 | }
366 |
367 | },
368 |
369 | _getLevel: function(item) {
370 |
371 | var level = 1;
372 |
373 | if (this.options.listType) {
374 | var list = item.closest(this.options.listType);
375 | while (list && list.length > 0 &&
376 | !list.is('.ui-sortable')) {
377 | level++;
378 | list = list.parent().closest(this.options.listType);
379 | }
380 | }
381 |
382 | return level;
383 | },
384 |
385 | _getChildLevels: function(parent, depth) {
386 | var self = this,
387 | o = this.options,
388 | result = 0;
389 | depth = depth || 0;
390 |
391 | $(parent).children(o.listType).children(o.items).each(function (index, child) {
392 | result = Math.max(self._getChildLevels(child, depth + 1), result);
393 | });
394 |
395 | return depth ? result + 1 : result;
396 | },
397 |
398 | _isAllowed: function(parentItem, level, levels) {
399 | var o = this.options,
400 | isRoot = $(this.domPosition.parent).hasClass('ui-sortable') ? true : false,
401 | maxLevels = this.placeholder.closest('.ui-sortable').nestedSortable('option', 'maxLevels'); // this takes into account the maxLevels set to the recipient list
402 |
403 | // Is the root protected?
404 | // Are we trying to nest under a no-nest?
405 | // Are we nesting too deep?
406 | if (!o.isAllowed(this.currentItem, parentItem) ||
407 | parentItem && parentItem.hasClass(o.disableNesting) ||
408 | o.protectRoot && (parentItem == null && !isRoot || isRoot && level > 1)) {
409 | this.placeholder.addClass(o.errorClass);
410 | if (maxLevels < levels && maxLevels != 0) {
411 | this.beyondMaxLevels = levels - maxLevels;
412 | } else {
413 | this.beyondMaxLevels = 1;
414 | }
415 | } else {
416 | if (maxLevels < levels && maxLevels != 0) {
417 | this.placeholder.addClass(o.errorClass);
418 | this.beyondMaxLevels = levels - maxLevels;
419 | } else {
420 | this.placeholder.removeClass(o.errorClass);
421 | this.beyondMaxLevels = 0;
422 | }
423 | }
424 | }
425 |
426 | }));
427 |
428 | $.mjs.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.mjs.nestedSortable.prototype.options);
429 | })(jQuery);
430 |
--------------------------------------------------------------------------------
/jbb_layers_panel/rb/states.rb:
--------------------------------------------------------------------------------
1 |
2 | module JBB_LayersPanel
3 |
4 | def self.resizeStates(width, height)
5 | #Extracted and modified from TT's SKUI project
6 | #http://github.com/thomthom/SKUI
7 | @dialogStates.set_size(width, height)
8 | @dialogStates.execute_script("getDialogSize();")
9 | jsonSize = @dialogStates.get_element_value("dialogSize")
10 | sizeHash = self.jsonToHash(jsonSize)
11 | dialog_width = sizeHash['width'].to_i
12 | dialog_height = sizeHash['height'].to_i
13 | adjust_width = width - dialog_width
14 | adjust_height = height - dialog_height
15 | unless adjust_width == 0 && adjust_height == 0
16 | new_width = width + adjust_width
17 | new_height = height + adjust_height
18 | @dialogStates.set_size( new_width, new_height )
19 | end
20 | #Correct height when target height is less than border height
21 | @dialogStates.execute_script("getDialogSize();")
22 | jsonSize = @dialogStates.get_element_value("dialogSize")
23 | sizeHash = self.jsonToHash(jsonSize)
24 | dialog_height = sizeHash['height'].to_i
25 | adjust_height = height - dialog_height
26 | i = 1
27 | while adjust_height != 0
28 | new_height = height + adjust_height + i
29 | @dialogStates.set_size( new_width, new_height )
30 | @dialogStates.execute_script("getDialogSize();")
31 | jsonSize = @dialogStates.get_element_value("dialogSize")
32 | sizeHash = self.jsonToHash(jsonSize)
33 | dialog_height = sizeHash['height'].to_i
34 | adjust_height = height - dialog_height
35 | i += 1
36 | end
37 | end#def
38 |
39 | def self.incStateDictID
40 | @stateDictID = @stateDictID + 1
41 | self.set_attribute(@model, "jbb_layerspanel", "stateDictID", @stateDictID) #Store incremented stateDictID in model attribute dict
42 | # puts "incStateDictID"
43 | end#def
44 |
45 | def self.initializeStateDictID
46 | if @stateDictID == nil
47 | if @model.get_attribute("jbb_layerspanel", "stateDictID") != nil #Get stateDictID from model if exists
48 | @stateDictID = @model.get_attribute("jbb_layerspanel", "stateDictID")
49 | else #Else, create it
50 | @stateDictID = 0
51 | end#if
52 | self.incStateDictID
53 | end#if
54 | end#def
55 |
56 | def self.storeStateSerialize
57 | # if @allowStateSerialize == true
58 | @dialogStates.execute_script("storeSerialize();")
59 | serialized = @dialogStates.get_element_value("serialize")
60 | self.set_attribute(@model, "jbb_layerspanel", "stateSerialized", serialized) #Store serialized in model attribute dict
61 | # end#if
62 | end#def
63 |
64 | def self.statesDialogStartup
65 | @allowStatesChange = false
66 | serialized = @model.get_attribute("jbb_layerspanel", "stateSerialized") #retreive string of serialized layers
67 | matches = serialized.to_s.scan(/(state|group)\[(\d+)\]\=(\d+|null)/) #make an array of it
68 |
69 | matches.each do |match|
70 | #match[0] : state or group
71 | #match[1] : ID
72 | #match[2] : parent ID
73 | match = match.to_a
74 |
75 | if match[0].to_s == "state" #if state
76 | stateName = @model.get_attribute("jbb_layerspanel_states", match[1])
77 | addState = "addState('#{stateName}', '#{match[1]}', '#{match[2]}', false);"
78 | @dialogStates.execute_script(addState)
79 | else #if group
80 | groupName = @model.get_attribute("jbb_layerspanel_statesGroups", match[1])
81 | addGroup = "addGroup('#{groupName}', '#{match[1]}', '#{match[2]}');"
82 | @dialogStates.execute_script(addGroup)
83 | end#if
84 | end#each
85 | self.getCollapsedStatesGroups()
86 | @allowStatesChange = true
87 | end#def
88 |
89 | def self.refreshStatesDialog
90 | @dialogStates.execute_script("emptyOl();") if @dialogStates != nil
91 | self.statesDialogStartup
92 | end#def
93 |
94 | def self.updateState(stateID)
95 | context = self.currentContext
96 | visibleLayers = Array.new
97 | visibleGroups = Array.new
98 | hiddenByGroupsLayers = Array.new
99 | hiddenByGroupsGroups = Array.new
100 | collapsedGroups = Array.new
101 | @layers.each{|layer|
102 | layerID = layer.get_attribute("jbb_layerspanel", "ID")
103 | if layer.visible?
104 | visibleLayers.push(layer.get_attribute("jbb_layerspanel", "ID").to_i)
105 | elsif context.get_attribute("jbb_layerspanel_tempHiddenByGroupLayers", layerID) == 1
106 | hiddenByGroupsLayers.push(layer.get_attribute("jbb_layerspanel", "ID").to_i)
107 | end#if
108 | }
109 | groups = context.attribute_dictionaries["jbb_layerspanel_groups"]
110 | if groups != nil
111 | groups.each { | groupID, value |
112 | if context.get_attribute("jbb_layerspanel_tempHiddenGroups", groupID).to_i == 1
113 | elsif context.get_attribute("jbb_layerspanel_tempHiddenGroups", groupID).to_i == 2
114 | hiddenByGroupsGroups.push(groupID.to_i)
115 | else
116 | visibleGroups.push(groupID.to_i)
117 | end#if
118 | if context.get_attribute("jbb_layerspanel_collapseGroups", groupID).to_i == 1
119 | collapsedGroups.push(groupID.to_i)
120 | end#if
121 | }
122 | end#if
123 |
124 | self.set_attribute(@model, "jbb_layerspanel_states", stateID.to_s + "activeLayerID", @model.active_layer.get_attribute("jbb_layerspanel", "ID").to_i)
125 | self.set_attribute(@model, "jbb_layerspanel_states", stateID.to_s + "visibleLayers", visibleLayers)
126 | self.set_attribute(@model, "jbb_layerspanel_states", stateID.to_s + "visibleGroups", visibleGroups)
127 | self.set_attribute(@model, "jbb_layerspanel_states", stateID.to_s + "hiddenByGroupsLayers", hiddenByGroupsLayers)
128 | self.set_attribute(@model, "jbb_layerspanel_states", stateID.to_s + "hiddenByGroupsGroups", hiddenByGroupsGroups)
129 | self.set_attribute(@model, "jbb_layerspanel_states", stateID.to_s + "collapsedGroups", collapsedGroups)
130 | end#def
131 |
132 | def self.getCollapsedStatesGroups()
133 | serialized = @model.get_attribute("jbb_layerspanel", "stateSerialized") #retreive string of serialized layers
134 | matches = serialized.to_s.scan(/(layer|group)\[(\d+)\]\=(\d+|null)/) #make an array of it
135 |
136 | matches.each do |match| #Group collapsing/expanding
137 | #match[0] : layer or group
138 | #match[1] : ID
139 | #match[2] : parent ID
140 | match = match.to_a
141 |
142 | if match[0].to_s == "group" #if group
143 | # puts match[1]
144 | context = self.currentContext
145 |
146 | if context.get_attribute("jbb_layerspanel_collapseStatesGroups", match[1]).to_i == 1
147 | # puts match[1]
148 | collapseFromRuby = "collapseFromRuby('#{match[1]}');"
149 | @dialogStates.execute_script(collapseFromRuby)
150 | end#if
151 | end#if
152 | end#each
153 | end#def
154 |
155 |
156 | ### STATES DIALOG ### ------------------------------------------------------
157 |
158 | # Create the WebDialog instance
159 | def self.createDialogStates
160 | @dialogStates = WebdialogBridge.new("Layer States", false, "LayersPanelStates", 215, 300, 300, 200, true)
161 | @dialogStates.min_width = 199
162 | @dialogStates.min_height = 37
163 | @dialogStates.set_file(@html_path6)
164 |
165 |
166 | ### Initialize dialog ### ------------------------------------------------------
167 |
168 | @dialogStates.add_bridge_callback("startup") do |wdl, action|
169 | self.statesDialogStartup
170 | end#callback
171 |
172 |
173 | ### States ### ------------------------------------------------------
174 |
175 | @dialogStates.add_bridge_callback("addStateStart") do |wdl, stateName|
176 | @model.start_operation("Add layer state", true)
177 | self.initializeStateDictID
178 | self.incStateDictID
179 | # puts stateName
180 | # puts @stateDictID
181 | self.set_attribute(@model, "jbb_layerspanel_states", @stateDictID, stateName)
182 | @previousState = @stateDictID
183 | self.updateState(@stateDictID)
184 | end#callback
185 |
186 | @dialogStates.add_bridge_callback("addStateEnd") do |wdl, allowSerialize|
187 | # allowSerialize == "true" ? self.storeStateSerialize :
188 | self.storeStateSerialize
189 | @model.commit_operation
190 | end#callback
191 |
192 | @previousState = 0
193 | @dialogStates.add_bridge_callback("setActiveStateFromJS") do |wdl, stateID|
194 | @allowStatesChange = false
195 | @model.start_operation("Change layers state", true)
196 | if stateID.to_i != 0 && @previousState == 0
197 | self.updateState(0)
198 | end#if
199 |
200 | if @model.get_attribute("jbb_layerspanel_states", stateID.to_s + "visibleLayers") != nil #Make sure there is something to read
201 | context = self.currentContext
202 | groups = context.attribute_dictionaries["jbb_layerspanel_groups"]
203 |
204 | #Set active layer
205 | activeLayerID = @model.get_attribute("jbb_layerspanel_states", stateID.to_s + "activeLayerID")
206 | @layers.each{|layer|
207 | if layer.get_attribute("jbb_layerspanel", "ID").to_i == activeLayerID.to_i
208 | layer.visible = true
209 | @model.active_layer = layer
210 | break
211 | end#if
212 | }
213 |
214 | #Hide all layers and groups
215 | @layers.each{|layer|
216 | layer.visible = false
217 | self.unHideByGroup(layer.get_attribute("jbb_layerspanel", "ID").to_i)
218 | }
219 | if groups != nil
220 | groups.each { | groupID, value |
221 | self.hideGroup(groupID, false)
222 | }
223 | end#if
224 |
225 | visibleLayers = @model.get_attribute("jbb_layerspanel_states", stateID.to_s + "visibleLayers")
226 | puts visibleGroups = @model.get_attribute("jbb_layerspanel_states", stateID.to_s + "visibleGroups")
227 | hiddenByGroupsLayers = @model.get_attribute("jbb_layerspanel_states", stateID.to_s + "hiddenByGroupsLayers")
228 | hiddenByGroupsGroups = @model.get_attribute("jbb_layerspanel_states", stateID.to_s + "hiddenByGroupsGroups")
229 | collapsedGroups = @model.get_attribute("jbb_layerspanel_states", stateID.to_s + "collapsedGroups")
230 |
231 | visibleLayers = [] if !visibleLayers
232 | visibleGroups = [] if !visibleGroups
233 | hiddenByGroupsLayers = [] if !hiddenByGroupsLayers
234 | hiddenByGroupsGroups = [] if !hiddenByGroupsGroups
235 | collapsedGroups = [] if !collapsedGroups
236 |
237 | #Unhide visible layers and groups
238 | visibleLayers.each{|layerID|
239 | @layers.each{|layer|
240 | if layer.get_attribute("jbb_layerspanel", "ID").to_i == layerID.to_i
241 | layer.visible = true
242 | break
243 | end#if
244 | }
245 | }
246 | visibleGroups.each{|groupID|
247 | self.unHideGroup(groupID)
248 | }
249 |
250 | #Set hiddenByGroup tags
251 | hiddenByGroupsLayers.each{|layerID|
252 | @layers.each{|layer|
253 | if layer.get_attribute("jbb_layerspanel", "ID").to_i == layerID.to_i
254 | self.hideByGroup(layer.get_attribute("jbb_layerspanel", "ID").to_i)
255 | break
256 | end#if
257 | }
258 | }
259 | hiddenByGroupsGroups.each{|groupID|
260 | self.hideGroup(groupID, true)
261 | }
262 |
263 | #Expand all groups
264 | if context.attribute_dictionaries["jbb_layerspanel_collapseGroups"] != nil
265 | context.attribute_dictionaries["jbb_layerspanel_collapseGroups"].each{|groupID, value|
266 | self.set_attribute(context, "jbb_layerspanel_collapseGroups", groupID, 0)
267 | }
268 | end#if
269 | #Collapse groups
270 | collapsedGroups.each{|groupID|
271 | self.set_attribute(context, "jbb_layerspanel_collapseGroups", groupID, 1)
272 | }
273 |
274 | self.refreshDialog
275 | end#if
276 | @model.commit_operation
277 | @allowStatesChange = true
278 | @previousState = stateID.to_i
279 | end#callback
280 |
281 | @dialogStates.add_bridge_callback("updateState") do |wdl, stateID|
282 | @model.start_operation("Update State", true)
283 | self.updateState(stateID)
284 | @model.commit_operation
285 | end#callback
286 |
287 | @dialogStates.add_bridge_callback("renameState") do |wdl, renameState|
288 | @model.start_operation("Rename layer state", true)
289 | hashState = self.jsonToHash(renameState)
290 | stateID = hashState['stateID']
291 | # puts stateID
292 | newStateName = hashState['newStateName']
293 | # puts newStateName
294 | self.set_attribute(@model, "jbb_layerspanel_states", stateID, newStateName) #Store new state's name from ID
295 | @model.commit_operation
296 | end#callback
297 |
298 |
299 | ### Groups ### ------------------------------------------------------
300 |
301 | @dialogStates.add_bridge_callback("addGroupStart") do |wdl, groupName|
302 | @model.start_operation("Add layer state group", true)
303 | self.initializeStateDictID
304 | self.incStateDictID
305 | # puts groupName
306 | # puts @stateDictID
307 | self.set_attribute(@model, "jbb_layerspanel_statesGroups", @stateDictID, groupName)
308 | end#callback
309 |
310 | @dialogStates.add_bridge_callback("addGroupEnd") do |wdl, allowSerialize|
311 | # allowSerialize == "true" ? self.storeStateSerialize :
312 | self.storeStateSerialize
313 | @model.commit_operation
314 | end#callback
315 |
316 | @dialogStates.add_bridge_callback("groupStates") do |wdl, action|
317 | @model.start_operation("Group layer states", true, false, true) #merges with previous "Add group" operation
318 | self.storeStateSerialize
319 | @model.commit_operation
320 | end#callback
321 |
322 | @dialogStates.add_bridge_callback("unGroupStates") do |wdl, action|
323 | @model.start_operation("Ungroup layer states", true)
324 | self.storeStateSerialize
325 | @model.commit_operation
326 | end#callback
327 |
328 | @dialogStates.add_bridge_callback("collapseGroup") do |wdl, groupID|
329 | # puts "collapse " + groupID
330 | @model.start_operation("Collapse group layer", true)
331 | self.set_attribute(self.currentContext, "jbb_layerspanel_collapseStatesGroups", groupID, 1)
332 | @model.commit_operation
333 | end#callback
334 |
335 | @dialogStates.add_bridge_callback("expandGroup") do |wdl, groupID|
336 | # puts "expand " + groupID
337 | @model.start_operation("Expand group layer", true)
338 | self.set_attribute(self.currentContext, "jbb_layerspanel_collapseStatesGroups", groupID, 0)
339 | @model.commit_operation
340 | end#callback
341 |
342 | @dialogStates.add_bridge_callback("getCollapsedGroups") do |wdl, a|
343 | self.getCollapsedStatesGroups()
344 | end#callback
345 |
346 | @dialogStates.add_bridge_callback("renameGroup") do |wdl, renameGroup|
347 | @model.start_operation("Rename layer state group", true)
348 | hashGroup = self.jsonToHash(renameGroup)
349 | groupID = hashGroup['groupID']
350 | # puts groupID
351 | newGroupName = hashGroup['newGroupName']
352 | # puts newGroupName
353 | self.set_attribute(@model, "jbb_layerspanel_statesGroups", groupID, newGroupName) #Store new group's name from ID
354 | @model.commit_operation
355 | end#callback
356 |
357 |
358 | ### Misc ### ------------------------------------------------------
359 |
360 | @dialogStates.add_bridge_callback("getStateDictID") do |wdl, act|
361 | if @stateDictID == nil
362 | self.initializeStateDictID
363 | end#if
364 | sendStateDictID = "receiveStateDictID('#{@stateDictID}');"
365 | @dialogStates.execute_script(sendStateDictID)
366 | end#callback
367 |
368 | @dialogStates.add_bridge_callback("minimizeDialog") do |wdl, size|
369 | sizeHash = self.jsonToHash(size)
370 | self.resizeStates(sizeHash['width'].to_i, 10)
371 | @heightBeforeMinimizeStates = sizeHash['height']
372 | end#callback
373 |
374 | @dialogStates.add_bridge_callback("maximizeDialog") do |wdl, width|
375 | width = width.to_i
376 | height = @heightBeforeMinimizeStates
377 | self.resizeStates(width, height)
378 | end#callback
379 |
380 | @dialogStates.add_bridge_callback("sortItem") do |wdl, serialized|
381 | @model.start_operation("Sort layer state", true)
382 | self.storeStateSerialize
383 | @model.commit_operation
384 | end#callback
385 |
386 | @dialogStates.add_bridge_callback("storeSerialize") do |wdl, serialized|
387 | @model.start_operation("Sort layer state", true)
388 | self.storeStateSerialize
389 | @model.commit_operation
390 | end#callback
391 |
392 | @dialogStates.add_bridge_callback("delete") do |wdl, serialized|
393 | @model.start_operation("Delete layer state", true)
394 | self.storeStateSerialize
395 | @model.commit_operation
396 | end#callback
397 |
398 | @dialogStates.add_bridge_callback("undo") do
399 | Sketchup.send_action("editUndo:")
400 | end#callback
401 |
402 | @dialogStates.add_bridge_callback("redo") do
403 | Sketchup.send_action("editRedo:")
404 | end#callback
405 |
406 | end#def
407 |
408 | def self.show_layerspanel_dlg_states
409 | if !@dialogStates || !@dialogStates.visible?
410 | self.createDialogStates
411 | self.showDialog(@dialogStates)
412 | self.make_toolwindow_frame("Layer States")
413 | @dialogStates.execute_script("window.blur()")
414 | end#if
415 | end#def
416 |
417 | def self.close_layerspanel_dlg_states
418 | if @dialogStates && @dialogStates.visible?
419 | @dialogStates.close
420 | end#if
421 | end#def
422 |
423 |
424 | end#module
--------------------------------------------------------------------------------
/jbb_layers_panel/js/jquery.mjs.nestedSortable.js:
--------------------------------------------------------------------------------
1 | /*
2 | * jQuery UI Nested Sortable
3 | * v 2.0 / 29 oct 2012
4 | * http://mjsarfatti.com/sandbox/nestedSortable
5 | *
6 | * Depends on:
7 | * jquery.ui.sortable.js 1.10+
8 | *
9 | * Copyright (c) 2010-2013 Manuele J Sarfatti
10 | * Licensed under the MIT License
11 | * http://www.opensource.org/licenses/mit-license.php
12 | */
13 |
14 | (function($) {
15 |
16 | function isOverAxis( x, reference, size ) {
17 | return ( x > reference ) && ( x < ( reference + size ) );
18 | }
19 |
20 | $.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
21 |
22 | options: {
23 | doNotClear: false,
24 | expandOnHover: 700,
25 | isAllowed: function(placeholder, placeholderParent, originalItem) { return true; },
26 | isTree: false,
27 | listType: 'ol',
28 | maxLevels: 0,
29 | protectRoot: false,
30 | rootID: null,
31 | rtl: false,
32 | startCollapsed: false,
33 | tabSize: 20,
34 |
35 | branchClass: 'mjs-nestedSortable-branch',
36 | collapsedClass: 'mjs-nestedSortable-collapsed',
37 | disableNestingClass: 'mjs-nestedSortable-no-nesting',
38 | errorClass: 'mjs-nestedSortable-error',
39 | expandedClass: 'mjs-nestedSortable-expanded',
40 | hoveringClass: 'mjs-nestedSortable-hovering',
41 | leafClass: 'mjs-nestedSortable-leaf'
42 | },
43 |
44 | _create: function() {
45 | this.element.data('ui-sortable', this.element.data('mjs-nestedSortable'));
46 |
47 | // mjs - prevent browser from freezing if the HTML is not correct
48 | if (!this.element.is(this.options.listType))
49 | throw new Error('nestedSortable: Please check that the listType option is set to your actual list type');
50 |
51 | // mjs - force 'intersect' tolerance method if we have a tree with expanding/collapsing functionality
52 | if (this.options.isTree) this.options.tolerance = 'intersect';
53 |
54 | $.ui.sortable.prototype._create.apply(this, arguments);
55 |
56 | // mjs - prepare the tree by applying the right classes (the CSS is responsible for actual hide/show functionality)
57 | if (this.options.isTree) {
58 | var self = this;
59 | $(this.items).each(function() {
60 | var $li = this.item;
61 | if ($li.children(self.options.listType).length) {
62 | $li.addClass(self.options.branchClass);
63 | // expand/collapse class only if they have children
64 | if (self.options.startCollapsed) $li.addClass(self.options.collapsedClass);
65 | else $li.addClass(self.options.expandedClass);
66 | } else {
67 | $li.addClass(self.options.leafClass);
68 | }
69 | })
70 | }
71 | },
72 |
73 | _destroy: function() {
74 | this.element
75 | .removeData("mjs-nestedSortable")
76 | .removeData("ui-sortable");
77 | return $.ui.sortable.prototype._destroy.apply(this, arguments);
78 | },
79 |
80 | _mouseDrag: function(event) {
81 | var i, item, itemElement, intersection,
82 | o = this.options,
83 | scrolled = false;
84 |
85 | //Compute the helpers position
86 | this.position = this._generatePosition(event);
87 | this.positionAbs = this._convertPositionTo("absolute");
88 |
89 | if (!this.lastPositionAbs) {
90 | this.lastPositionAbs = this.positionAbs;
91 | }
92 |
93 | //Do scrolling
94 | if(this.options.scroll) {
95 | if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
96 |
97 | if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
98 | this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
99 | } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
100 | this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
101 | }
102 |
103 | if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
104 | this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
105 | } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
106 | this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
107 | }
108 |
109 | } else {
110 |
111 | if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
112 | scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
113 | } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
114 | scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
115 | }
116 |
117 | if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
118 | scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
119 | } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
120 | scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
121 | }
122 |
123 | }
124 |
125 | if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
126 | $.ui.ddmanager.prepareOffsets(this, event);
127 | }
128 |
129 | //Regenerate the absolute position used for position checks
130 | this.positionAbs = this._convertPositionTo("absolute");
131 |
132 | // mjs - find the top offset before rearrangement,
133 | var previousTopOffset = this.placeholder.offset().top;
134 |
135 | //Set the helper position
136 | if(!this.options.axis || this.options.axis !== "y") {
137 | this.helper[0].style.left = this.position.left+"px";
138 | }
139 | if(!this.options.axis || this.options.axis !== "x") {
140 | this.helper[0].style.top = this.position.top+"px";
141 | }
142 |
143 | // mjs - check and reset hovering state at each cycle
144 | this.hovering = this.hovering ? this.hovering : null;
145 | this.mouseentered = this.mouseentered ? this.mouseentered : false;
146 |
147 | // mjs - let's start caching some variables
148 | var parentItem = (this.placeholder[0].parentNode.parentNode &&
149 | $(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
150 | ? $(this.placeholder[0].parentNode.parentNode)
151 | : null,
152 | level = this._getLevel(this.placeholder),
153 | childLevels = this._getChildLevels(this.helper);
154 |
155 | var newList = document.createElement(o.listType);
156 |
157 | //Rearrange
158 | for (i = this.items.length - 1; i >= 0; i--) {
159 |
160 | //Cache variables and intersection, continue if no intersection
161 | item = this.items[i];
162 | itemElement = item.item[0];
163 | intersection = this._intersectsWithPointer(item);
164 | if (!intersection) {
165 | continue;
166 | }
167 |
168 | // Only put the placeholder inside the current Container, skip all
169 | // items form other containers. This works because when moving
170 | // an item from one container to another the
171 | // currentContainer is switched before the placeholder is moved.
172 | //
173 | // Without this moving items in "sub-sortables" can cause the placeholder to jitter
174 | // beetween the outer and inner container.
175 | if (item.instance !== this.currentContainer) {
176 | continue;
177 | }
178 |
179 | // cannot intersect with itself
180 | // no useless actions that have been done before
181 | // no action if the item moved is the parent of the item checked
182 | if (itemElement !== this.currentItem[0] &&
183 | this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
184 | !$.contains(this.placeholder[0], itemElement) &&
185 | (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
186 | ) {
187 |
188 | // mjs - we are intersecting an element: trigger the mouseenter event and store this state
189 | if (!this.mouseentered) {
190 | $(itemElement).mouseenter();
191 | this.mouseentered = true;
192 | }
193 |
194 | // mjs - if the element has children and they are hidden, show them after a delay (CSS responsible)
195 | if (o.isTree && $(itemElement).hasClass(o.collapsedClass) && o.expandOnHover) {
196 | if (!this.hovering) {
197 | $(itemElement).addClass(o.hoveringClass);
198 | var self = this;
199 | this.hovering = window.setTimeout(function() {
200 | $(itemElement).removeClass(o.collapsedClass).addClass(o.expandedClass);
201 | self.refreshPositions();
202 | self._trigger("expand", event, self._uiHash());
203 | }, o.expandOnHover);
204 | }
205 | }
206 |
207 | this.direction = intersection == 1 ? "down" : "up";
208 |
209 | // mjs - rearrange the elements and reset timeouts and hovering state
210 | if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
211 | $(itemElement).mouseleave();
212 | this.mouseentered = false;
213 | $(itemElement).removeClass(o.hoveringClass);
214 | this.hovering && window.clearTimeout(this.hovering);
215 | this.hovering = null;
216 |
217 | // mjs - do not switch container if it's a root item and 'protectRoot' is true
218 | // or if it's not a root item but we are trying to make it root
219 | if (o.protectRoot
220 | && ! (this.currentItem[0].parentNode == this.element[0] // it's a root item
221 | && itemElement.parentNode != this.element[0]) // it's intersecting a non-root item
222 | ) {
223 | if (this.currentItem[0].parentNode != this.element[0]
224 | && itemElement.parentNode == this.element[0]
225 | ) {
226 |
227 | if ( ! $(itemElement).children(o.listType).length) {
228 | itemElement.appendChild(newList);
229 | o.isTree && $(itemElement).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
230 | }
231 |
232 | var a = this.direction === "down" ? $(itemElement).prev().children(o.listType) : $(itemElement).children(o.listType);
233 | if (a[0] !== undefined) {
234 | this._rearrange(event, null, a);
235 | }
236 |
237 | } else {
238 | this._rearrange(event, item);
239 | }
240 | } else if ( ! o.protectRoot) {
241 | this._rearrange(event, item);
242 | }
243 | } else {
244 | break;
245 | }
246 |
247 | // Clear emtpy ul's/ol's
248 | this._clearEmpty(itemElement);
249 |
250 | this._trigger("change", event, this._uiHash());
251 | break;
252 | }
253 | }
254 |
255 | // mjs - to find the previous sibling in the list, keep backtracking until we hit a valid list item.
256 | var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
257 | if (previousItem != null) {
258 | while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0] == this.currentItem[0] || previousItem[0] == this.helper[0]) {
259 | if (previousItem[0].previousSibling) {
260 | previousItem = $(previousItem[0].previousSibling);
261 | } else {
262 | previousItem = null;
263 | break;
264 | }
265 | }
266 | }
267 |
268 | // mjs - to find the next sibling in the list, keep stepping forward until we hit a valid list item.
269 | var nextItem = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null;
270 | if (nextItem != null) {
271 | while (nextItem[0].nodeName.toLowerCase() != 'li' || nextItem[0] == this.currentItem[0] || nextItem[0] == this.helper[0]) {
272 | if (nextItem[0].nextSibling) {
273 | nextItem = $(nextItem[0].nextSibling);
274 | } else {
275 | nextItem = null;
276 | break;
277 | }
278 | }
279 | }
280 |
281 | this.beyondMaxLevels = 0;
282 |
283 | // mjs - if the item is moved to the left, send it one level up but only if it's at the bottom of the list
284 | if (parentItem != null
285 | && nextItem == null
286 | && ! (o.protectRoot && parentItem[0].parentNode == this.element[0])
287 | &&
288 | (o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth())
289 | || ! o.rtl && (this.positionAbs.left < parentItem.offset().left))
290 | ) {
291 |
292 | parentItem.after(this.placeholder[0]);
293 | if (o.isTree && parentItem.children(o.listItem).children('li:visible:not(.ui-sortable-helper)').length < 1) {
294 | parentItem.removeClass(this.options.branchClass + ' ' + this.options.expandedClass)
295 | .addClass(this.options.leafClass);
296 | }
297 | this._clearEmpty(parentItem[0]);
298 | this._trigger("change", event, this._uiHash());
299 | }
300 | // mjs - if the item is below a sibling and is moved to the right, make it a child of that sibling
301 | else if (previousItem != null
302 | && ! previousItem.hasClass(o.disableNestingClass)
303 | &&
304 | (previousItem.children(o.listType).length && previousItem.children(o.listType).is(':visible')
305 | || ! previousItem.children(o.listType).length)
306 | && ! (o.protectRoot && this.currentItem[0].parentNode == this.element[0])
307 | &&
308 | (o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize)
309 | || ! o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize))
310 | ) {
311 |
312 | this._isAllowed(previousItem, level, level+childLevels+1);
313 |
314 | if (!previousItem.children(o.listType).length) {
315 | previousItem[0].appendChild(newList);
316 | o.isTree && previousItem.removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
317 | }
318 |
319 | // mjs - if this item is being moved from the top, add it to the top of the list.
320 | if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) {
321 | previousItem.children(o.listType).prepend(this.placeholder);
322 | }
323 | // mjs - otherwise, add it to the bottom of the list.
324 | else {
325 | previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
326 | }
327 |
328 | this._trigger("change", event, this._uiHash());
329 | }
330 | else {
331 | this._isAllowed(parentItem, level, level+childLevels);
332 | }
333 |
334 | //Post events to containers
335 | this._contactContainers(event);
336 |
337 | //Interconnect with droppables
338 | if($.ui.ddmanager) {
339 | $.ui.ddmanager.drag(this, event);
340 | }
341 |
342 | //Call callbacks
343 | this._trigger('sort', event, this._uiHash());
344 |
345 | this.lastPositionAbs = this.positionAbs;
346 | return false;
347 |
348 | },
349 |
350 | _mouseStop: function(event, noPropagation) {
351 |
352 | // mjs - if the item is in a position not allowed, send it back
353 | if (this.beyondMaxLevels) {
354 |
355 | this.placeholder.removeClass(this.options.errorClass);
356 |
357 | if (this.domPosition.prev) {
358 | $(this.domPosition.prev).after(this.placeholder);
359 | } else {
360 | $(this.domPosition.parent).prepend(this.placeholder);
361 | }
362 |
363 | this._trigger("revert", event, this._uiHash());
364 |
365 | }
366 |
367 |
368 | // mjs - clear the hovering timeout, just to be sure
369 | $('.'+this.options.hoveringClass).mouseleave().removeClass(this.options.hoveringClass);
370 | this.mouseentered = false;
371 | this.hovering && window.clearTimeout(this.hovering);
372 | this.hovering = null;
373 |
374 | $.ui.sortable.prototype._mouseStop.apply(this, arguments);
375 |
376 | },
377 |
378 | // mjs - this function is slightly modified to make it easier to hover over a collapsed element and have it expand
379 | _intersectsWithSides: function(item) {
380 |
381 | var half = this.options.isTree ? .8 : .5;
382 |
383 | var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height*half), item.height),
384 | isOverTopHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top - (item.height*half), item.height),
385 | isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
386 | verticalDirection = this._getDragVerticalDirection(),
387 | horizontalDirection = this._getDragHorizontalDirection();
388 |
389 | if (this.floating && horizontalDirection) {
390 | return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
391 | } else {
392 | return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && isOverTopHalf));
393 | }
394 |
395 | },
396 |
397 | _contactContainers: function(event) {
398 |
399 | if (this.options.protectRoot && this.currentItem[0].parentNode == this.element[0] ) {
400 | return;
401 | }
402 |
403 | $.ui.sortable.prototype._contactContainers.apply(this, arguments);
404 |
405 | },
406 |
407 | _clear: function(event, noPropagation) {
408 |
409 | $.ui.sortable.prototype._clear.apply(this, arguments);
410 |
411 | // mjs - clean last empty ul/ol
412 | for (var i = this.items.length - 1; i >= 0; i--) {
413 | var item = this.items[i].item[0];
414 | this._clearEmpty(item);
415 | }
416 |
417 | },
418 |
419 | serialize: function(options) {
420 |
421 | var o = $.extend({}, this.options, options),
422 | items = this._getItemsAsjQuery(o && o.connected),
423 | str = [];
424 |
425 | $(items).each(function() {
426 | var res = ($(o.item || this).attr(o.attribute || 'id') || '')
427 | .match(o.expression || (/(.+)[-=_](.+)/)),
428 | pid = ($(o.item || this).parent(o.listType)
429 | .parent(o.items)
430 | .attr(o.attribute || 'id') || '')
431 | .match(o.expression || (/(.+)[-=_](.+)/));
432 |
433 | if (res) {
434 | str.push(((o.key || res[1]) + '[' + (o.key && o.expression ? res[1] : res[2]) + ']')
435 | + '='
436 | + (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID));
437 | }
438 | });
439 |
440 | if(!str.length && o.key) {
441 | str.push(o.key + '=');
442 | }
443 |
444 | return str.join('&');
445 |
446 | },
447 |
448 | toHierarchy: function(options) {
449 |
450 | var o = $.extend({}, this.options, options),
451 | sDepth = o.startDepthCount || 0,
452 | ret = [];
453 |
454 | $(this.element).children(o.items).each(function () {
455 | var level = _recursiveItems(this);
456 | ret.push(level);
457 | });
458 |
459 | return ret;
460 |
461 | function _recursiveItems(item) {
462 | var id = ($(item).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
463 | if (id) {
464 | var currentItem = {"id" : id[2]};
465 | if ($(item).children(o.listType).children(o.items).length > 0) {
466 | currentItem.children = [];
467 | $(item).children(o.listType).children(o.items).each(function() {
468 | var level = _recursiveItems(this);
469 | currentItem.children.push(level);
470 | });
471 | }
472 | return currentItem;
473 | }
474 | }
475 | },
476 |
477 | toArray: function(options) {
478 |
479 | var o = $.extend({}, this.options, options),
480 | sDepth = o.startDepthCount || 0,
481 | ret = [],
482 | left = 2;
483 |
484 | ret.push({
485 | "item_id": o.rootID,
486 | "parent_id": 'none',
487 | "depth": sDepth,
488 | "left": '1',
489 | "right": ($(o.items, this.element).length + 1) * 2
490 | });
491 |
492 | $(this.element).children(o.items).each(function () {
493 | left = _recursiveArray(this, sDepth + 1, left);
494 | });
495 |
496 | ret = ret.sort(function(a,b){ return (a.left - b.left); });
497 |
498 | return ret;
499 |
500 | function _recursiveArray(item, depth, left) {
501 |
502 | var right = left + 1,
503 | id,
504 | pid;
505 |
506 | if ($(item).children(o.listType).children(o.items).length > 0) {
507 | depth ++;
508 | $(item).children(o.listType).children(o.items).each(function () {
509 | right = _recursiveArray($(this), depth, right);
510 | });
511 | depth --;
512 | }
513 |
514 | id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));
515 |
516 | if (depth === sDepth + 1) {
517 | pid = o.rootID;
518 | } else {
519 | var parentItem = ($(item).parent(o.listType)
520 | .parent(o.items)
521 | .attr(o.attribute || 'id'))
522 | .match(o.expression || (/(.+)[-=_](.+)/));
523 | pid = parentItem[2];
524 | }
525 |
526 | if (id) {
527 | ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
528 | }
529 |
530 | left = right + 1;
531 | return left;
532 | }
533 |
534 | },
535 |
536 | _clearEmpty: function(item) {
537 | var o = this.options;
538 |
539 | var emptyList = $(item).children(o.listType);
540 |
541 | if (emptyList.length && !emptyList.children().length && !o.doNotClear) {
542 | if($(item).hasClass("layer")){ // Added for Layers Panel
543 | o.isTree && $(item).removeClass(o.branchClass + ' ' + o.expandedClass).addClass(o.leafClass);
544 | emptyList.remove();
545 | }
546 | } else if (o.isTree && emptyList.length && emptyList.children().length && emptyList.is(':visible')) {
547 | $(item).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.expandedClass);
548 | } else if (o.isTree && emptyList.length && emptyList.children().length && !emptyList.is(':visible')) {
549 | $(item).removeClass(o.leafClass).addClass(o.branchClass + ' ' + o.collapsedClass);
550 | }
551 |
552 | },
553 |
554 | _getLevel: function(item) {
555 |
556 | var level = 1;
557 |
558 | if (this.options.listType) {
559 | var list = item.closest(this.options.listType);
560 | while (list && list.length > 0 &&
561 | !list.is('.ui-sortable')) {
562 | level++;
563 | list = list.parent().closest(this.options.listType);
564 | }
565 | }
566 |
567 | return level;
568 | },
569 |
570 | _getChildLevels: function(parent, depth) {
571 | var self = this,
572 | o = this.options,
573 | result = 0;
574 | depth = depth || 0;
575 |
576 | $(parent).children(o.listType).children(o.items).each(function (index, child) {
577 | result = Math.max(self._getChildLevels(child, depth + 1), result);
578 | });
579 |
580 | return depth ? result + 1 : result;
581 | },
582 |
583 | _isAllowed: function(parentItem, level, levels) {
584 | var o = this.options,
585 | maxLevels = this.placeholder.closest('.ui-sortable').nestedSortable('option', 'maxLevels'); // this takes into account the maxLevels set to the recipient list
586 |
587 | // mjs - is the root protected?
588 | // mjs - are we nesting too deep?
589 | if ( ! o.isAllowed(this.placeholder, parentItem, this.currentItem)) {
590 | this.placeholder.addClass(o.errorClass);
591 | if (maxLevels < levels && maxLevels != 0) {
592 | this.beyondMaxLevels = levels - maxLevels;
593 | } else {
594 | this.beyondMaxLevels = 1;
595 | }
596 | } else {
597 | if (maxLevels < levels && maxLevels != 0) {
598 | this.placeholder.addClass(o.errorClass);
599 | this.beyondMaxLevels = levels - maxLevels;
600 | } else {
601 | this.placeholder.removeClass(o.errorClass);
602 | this.beyondMaxLevels = 0;
603 | }
604 | }
605 | }
606 |
607 | }));
608 |
609 | $.mjs.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.mjs.nestedSortable.prototype.options);
610 | })(jQuery);
--------------------------------------------------------------------------------
/jbb_layers_panel/js/states.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | /////////////// BASE FUNCTIONS /////////////
5 |
6 | var WIN=false;
7 | var OSX=false;
8 | if (navigator.appVersion.indexOf("Win")!=-1) WIN=true;
9 | else if (navigator.appVersion.indexOf("Mac")!=-1) OSX=true;
10 |
11 | function reloadDialog() {
12 | location.reload();
13 | }
14 |
15 | function emptyOl() {
16 | $('#olsortable').empty();
17 | }
18 |
19 | function getDialogSize() {
20 | var size = { "height":$(window).height(), "width":$(window).width() }; //Json
21 | var jsonSize = $.toJSON( size );
22 | $('#dialogSize').val(jsonSize);
23 | }
24 |
25 | //-------------
26 |
27 | var allowSerialize = true;
28 |
29 | function makeUnselectable(node) {
30 | if (node.nodeType == 1) {
31 | node.setAttribute("unselectable", "on");
32 | }
33 | var child = node.firstChild;
34 | while (child) {
35 | makeUnselectable(child);
36 | child = child.nextSibling;
37 | }
38 |
39 | $('.inputNameInput').removeAttr('unselectable');
40 | $('body').removeAttr('unselectable');
41 | $('html').removeAttr('unselectable');
42 | $('ol').removeAttr('unselectable');
43 | $('input').removeAttr('unselectable');
44 | $('button').removeAttr('unselectable');
45 | $('label').removeAttr('unselectable');
46 | $('#confirmDelete').removeAttr('unselectable');
47 | $('#wrapper').removeAttr('unselectable');
48 | }
49 |
50 |
51 | //-------------
52 |
53 |
54 | function getModelStates(serialize) {
55 | allowSerialize = false;
56 | query = 'skp:getModelStates@' + serialize;
57 | skpCallback(query);
58 | allowSerialize = true;
59 | }
60 |
61 | //-------------
62 |
63 |
64 | function getActiveState() {
65 | skpCallback('skp:getActiveState@');
66 | }
67 |
68 | //-------------
69 |
70 |
71 | function getCollapsedGroups() {
72 | allowSerialize = false;
73 | skpCallback('skp:getCollapsedGroups@');
74 | allowSerialize = true;
75 | }
76 |
77 | //-------------
78 |
79 |
80 | function setActiveStateFromJS(stateID) {
81 | stateID = stateID.replace('state_', '')
82 | skpCallback('skp:setActiveStateFromJS@' + stateID);
83 | }
84 |
85 |
86 | //-------------
87 |
88 |
89 | function addState(stateName, stateID, parentID, appendToSelected) {
90 |
91 | allowSerialize = false;
92 | var newState = false;
93 | if (!stateID) { // stateID not set, new state created from js, else created from ruby
94 | newState = true;
95 | }
96 |
97 | if (newState) {
98 | //Check if State nb exists
99 | var stateNb = 1;
100 | while (true) {
101 | var stateName = 'State ' + stateNb;
102 | if ($("span").filter(function() { return ($(this).text() === stateName) }).length) {
103 | stateNb++;
104 | }
105 | else {
106 | break;
107 | }
108 | }
109 |
110 | skpCallback('skp:addStateStart@' + stateName);
111 |
112 | getStateDictID();
113 | stateID = stateDictID;
114 |
115 | allowSerialize = true;
116 | }
117 |
118 | var stateStr = ''
119 |
120 | if (parentID && parentID != "null"){ //ID parentID is set, find it and append to it
121 | parentID = '#group_' + parentID;
122 | $(parentID).children("ol").append(stateStr);
123 | }
124 | else { //else, append normally
125 | if($('.ui-selected').length > 0 && appendToSelected != false) { //if something selected
126 | if($('.ui-selected').last().parent().hasClass('group')){ //if append to group
127 | $('.ui-selected').last().parent().children('ol').append(stateStr);
128 | }
129 | else { $('.ui-selected').last().parent().after(stateStr); }
130 | }
131 | else {
132 | $('.sortable').append(stateStr);
133 | }
134 | }
135 |
136 | if (newState) {
137 | $(".active").removeClass("enabled").addClass("disabled");
138 | $("#state_" + stateID).children("div").children(".active").removeClass("disabled").addClass("enabled");
139 | skpCallback('skp:addStateEnd@' + allowSerialize);
140 | }
141 | allowSerialize = true;
142 | return stateID;
143 | }
144 |
145 |
146 | //-------------
147 |
148 |
149 | function addGroup(groupName, groupID, parentID, appendToSelected) {
150 |
151 | allowSerialize = false;
152 | var newGroup = false;
153 | if (!groupID) { // stateID not set, new state created from js, else created from ruby
154 | newGroup = true;
155 | }
156 |
157 | if (newGroup) {
158 | //Check if Group nb exists
159 | var groupNb = 1;
160 | while (true) {
161 | var groupName = 'Group ' + groupNb;
162 | if ($("span").filter(function() { return ($(this).text() === groupName) }).length) {
163 | groupNb++;
164 | }
165 | else {
166 | break;
167 | }
168 | }
169 |
170 | skpCallback('skp:addGroupStart@' + groupName);
171 |
172 | getStateDictID();
173 | groupID = stateDictID;
174 |
175 | allowSerialize = true;
176 | }
177 |
178 | var groupStr = '
';
179 |
180 | if (parentID && parentID != "null"){ //ID parentID is set, find it and append to it
181 | parentID = '#group_' + parentID;
182 | $(parentID).children("ol").append(groupStr);
183 | }
184 | else { //else, append normally
185 | if($('.ui-selected').length > 0 && appendToSelected != false) { //if something selected
186 | if($('.ui-selected').last().parent().hasClass('group')){ //if append to group
187 | $('.ui-selected').last().parent().children('ol').append(groupStr);
188 | }
189 | else { $('.ui-selected').last().parent().after(groupStr); }
190 | }
191 | else {
192 | $('.sortable').append(groupStr);
193 | }
194 | }
195 |
196 | if (newGroup) {
197 | skpCallback('skp:addGroupEnd@' + allowSerialize);
198 | }
199 | allowSerialize = true;
200 | return groupID;
201 | }
202 |
203 | function groupStates() { // Group selected states
204 | if($(".ui-selected").length){
205 | groupID = addGroup(undefined, undefined, undefined, false);
206 | groupID = "#group_" + groupID;
207 | groupOl = $(groupID).children("ol");
208 |
209 | firstSelected = $(".ui-selected:first").parent();
210 |
211 | $(groupID).insertAfter(firstSelected);
212 |
213 | $('.ui-selected').each(function() {
214 | var item = $(this).parent();
215 | if (!item.parent().parent().children(".lidiv").is(".ui-selected") && !item.is("#state_0")) { // If not nested in selected
216 | item.appendTo(groupOl);
217 | }
218 | });
219 | $('.ui-selected').each(function() {
220 | $(this).removeClass("ui-selected"); // Deselect all
221 | });
222 | $(groupID).children(".lidiv").addClass("ui-selected"); // Select new group
223 |
224 | skpCallback('skp:groupStates@');
225 | }
226 | }
227 |
228 | function unGroupStates() { // Ungroup selected group
229 | if($(".ui-selected").length){
230 | $(".ui-selected").each(function() {
231 | var item = $(this).parent();
232 | if (isGroup(item) && !item.parent().parent().children(".lidiv").is(".ui-selected")) { // If group && not nested in selected
233 | item.addClass("delete");
234 | }
235 | });
236 | $(".ui-selected").each(function() {
237 | var item = $(this).parent();
238 | if(isGroup(item)){
239 | if (item.hasClass("delete")) {
240 | item.children("ol").children("li").insertBefore(item);
241 | item.empty().remove();
242 | }
243 | skpCallback('skp:unGroupStates@');
244 | }
245 | });
246 | }
247 | }
248 |
249 | //-------------
250 |
251 | function renameState(stateID, newStateName) {
252 | var renameState = { "stateID":stateID, "newStateName":newStateName }; //Json
253 | renameState = $.toJSON( renameState );
254 | skpCallback('skp:renameState@' + renameState);
255 | }
256 |
257 | function renameGroup(groupID, newGroupName) {
258 | var renameGroup = { "groupID":groupID, "newGroupName":newGroupName }; //Json
259 | renameGroup = $.toJSON( renameGroup );
260 | skpCallback('skp:renameGroup@' + renameGroup);
261 | }
262 |
263 | function collapseFromRuby(groupID) {
264 | $("#group_" + groupID).removeClass('mjs-nestedSortable-expanded').addClass('mjs-nestedSortable-collapsed');
265 | }
266 |
267 |
268 | //-------------
269 |
270 |
271 | function trash() {
272 | $('.ui-selected').each(function() {
273 | if ($(this).parent().hasClass("group")) { // If Group
274 | $(this).parent().find('.lidiv').addClass('ui-selected'); // Select all nested states and groups
275 | }
276 | });
277 |
278 | var deleted = false;
279 | var isActive = false;
280 | $('.ui-selected').each(function() { // Then, delete groups
281 | deleted = true;
282 | $(this).parent().empty().remove();
283 | if($(this).find('.active').hasClass('enabled')){ isActive = true; }
284 | });
285 |
286 | if(deleted){ skpCallback('skp:delete@'); }
287 | if(isActive){ $("#state_0").find(".active").removeClass("disabled").addClass("enabled"); }
288 | }
289 |
290 |
291 | //-------------
292 |
293 |
294 | function getStateDictID() {
295 | skpCallback('skp:getStateDictID');
296 | }
297 |
298 |
299 | function receiveStateDictID(receivedStateDictID) {
300 | stateDictID = receivedStateDictID;
301 | }
302 |
303 |
304 | //-------------
305 |
306 |
307 | function storeSerialize() {
308 | if (allowSerialize == true) {
309 | serialized = $('ol.sortable').nestedSortable('serialize');
310 | $('#serialize').val(serialized);
311 | }
312 | }
313 |
314 |
315 | //-------------
316 |
317 |
318 | function visibilityChanged(stateID) {
319 | $(".active").removeClass("enabled").addClass("disabled");
320 | $("#state_0").find(".active").removeClass("disabled").addClass("enabled");
321 | }
322 |
323 |
324 |
325 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
326 |
327 |
328 |
329 |
330 |
331 | $(document).ready(function(){
332 |
333 |
334 | makeUnselectable(document);
335 |
336 | skpCallback('skp:startup@');
337 |
338 | var selectedItems = [];
339 | $('#states').selectable({
340 | cancel: ".active, .handle, .disclose, .inputName, .stateNameText",
341 | filter: ".lidiv",
342 | selecting: function(e, ui) { // Enable shift-click selection
343 | var curr = $(ui.selecting.tagName, e.target).index(ui.selecting); // get selecting item index
344 | if(e.shiftKey && prev > -1) { // if shift key was pressed and there is previous - select them all
345 | $(ui.selecting.tagName, e.target).slice(Math.min(prev, curr), 1 + Math.max(prev, curr)).filter(".lidiv").addClass('ui-selected');
346 | prev = -1; // and reset prev
347 | } else {
348 | prev = curr; // othervise just save prev
349 | }
350 | },
351 | stop: function(event, ui) {
352 | }
353 | });
354 |
355 | $(document).bind("contextmenu", function(e) { // Disable right-click
356 | return false;
357 | });
358 |
359 |
360 | $(document).click(function (e) {
361 | if(allowDeselect && !$(e.target).is('.disclose, .handle, .active, .inputName, .inputNameInput, .stateName, .stateNameText')) {
362 | if(!$(e.target).parent().is('.footerElement, .headerElement, .headerElement2, #renderListButton')) {
363 | if(!$(e.target).parents(".menu").length) {
364 | $('.ui-selected').removeClass('ui-selected');
365 | }
366 | }
367 | }
368 | });
369 |
370 | $(document).on('click', '.disclose', function (e) {
371 | var groupID = $(this).parent().parent().attr('id').replace('group_', '');
372 | if ($(this).closest('li').hasClass('mjs-nestedSortable-collapsed')) {
373 | $(this).closest('li').removeClass('mjs-nestedSortable-collapsed').addClass('mjs-nestedSortable-expanded');
374 | skpCallback('skp:expandGroup@' + groupID);
375 | }
376 | else if ($(this).closest('li').hasClass('mjs-nestedSortable-expanded')) {
377 | $(this).closest('li').removeClass('mjs-nestedSortable-expanded').addClass('mjs-nestedSortable-collapsed');
378 | skpCallback('skp:collapseGroup@' + groupID);
379 | }
380 | else {
381 | $(this).closest('li').addClass('mjs-nestedSortable-collapsed');
382 | skpCallback('skp:collapseGroup@' + groupID);
383 | }
384 | });
385 |
386 | $(document).on('click', '.active', function (e) {
387 | activeStateID = $(this).parent().parent().attr('id');
388 | activeStateID = activeStateID.replace('state_', '');
389 |
390 | if ($(this).parent().children(".visibility").hasClass("hidden")) { // State hidden
391 | showStateFromJS(activeStateID);
392 | }
393 |
394 | $(".active").removeClass("enabled").addClass("disabled");
395 | $(this).toggleClass("enabled").toggleClass("disabled");
396 | setActiveStateFromJS(activeStateID);
397 | });
398 |
399 |
400 |
401 |
402 | ////////////// SORTING /////////////////
403 | allowDeselect = true;
404 |
405 | $('ol.sortable')
406 | .nestedSortable({
407 | delay: 100,
408 | forcePlaceholderSize: true,
409 | handle: '.handle',
410 | helper: 'clone',
411 | items: 'li',
412 | opacity: .6,
413 | placeholder: 'placeholder',
414 | revert: 0,
415 | tabSize: 18,
416 | tolerance: 'pointer',
417 | toleranceElement: '> div',
418 |
419 | containment: $('#statesContainer'),
420 |
421 | isTree: true,
422 | expandOnHover: 500
423 | });
424 |
425 | var selected;
426 | $(document).on('mousedown', '.handle', function (e) {
427 | selected = false;
428 | if($(this).parent().hasClass("ui-selected")){
429 | selected = true;
430 | }
431 |
432 | if(selected == true && e.which == 1){ //e.which == 1 is left-click only
433 | $('body').append(''); // Create helpers container
434 | $('#helpers').css({
435 | position: "absolute",
436 | left: e.pageX-10,
437 | top: e.pageY-10
438 | });
439 | $(".ui-selected").each(function(e){
440 | if (!$(this).parent().hasClass("sorted")){ // If not already in #helpers (Usefull when mouseup outside dialog)
441 | if ($(this).parents("ol").parents().children(".ui-selected").length > 0){ // If nested in selected group, do nothing
442 | } else { // Else, put it in the helpers container
443 | $(this).parent().clone().addClass("sorted").css({
444 | width: $(this).parent().width(),
445 | opacity: 0.6
446 | }).appendTo("#helpers"); // Put them in helpers container
447 | }
448 | }
449 | });
450 | }
451 | });
452 |
453 | $( "ol.sortable" ).on( "sortstart", function( event, ui ) { // When an item is sorted
454 | allowDeselect = false;
455 |
456 | if(selected == true){
457 | ui.helper.remove();
458 | ui.item.remove();
459 | $(".ui-selected").parent().not(".sorted").each(function(){
460 | if ($(this).parents(".sorted").length > 0){
461 | } else { $(this).remove(); }
462 | });
463 | }
464 | });
465 |
466 | $( "ol.sortable" ).on( "sort", function( e, ui ) { // When an item is sorted
467 | if(selected == true){
468 | $('#helpers').css({
469 | position: "absolute",
470 | left: e.pageX-10,
471 | top: e.pageY-10
472 | });
473 | }
474 | });
475 |
476 | $( "ol.sortable" ).on( "sortbeforestop", function( e, ui ) {
477 | if(selected == true){
478 | lastSorted = ui.placeholder;
479 | $("#helpers").children().each(function(e){
480 | $(this).removeAttr('style').insertAfter(lastSorted);
481 | lastSorted = $(this);
482 | });
483 | $('#helpers').remove(); // Destroy the helpers container
484 | }
485 | });
486 |
487 | $( "ol.sortable" ).on( "sortstop", function( event, ui ) { // When an item is sorted
488 | allowDeselect = true;
489 |
490 | if(selected == false){
491 | ui.item.addClass("sorted");
492 | }
493 |
494 | $(".sorted").each(function(e){
495 | if ($(this).parents('.hiddenGroup').length > 0) { // If nested in hiddenGroup
496 | if ($(this).children(".lidiv").children(".visibility").hasClass("visible")) { //if item visible
497 | hide($(this), true);
498 | }
499 | }
500 | else { //sorted outside group
501 | if ($(this).children(".lidiv").children(".visibility").hasClass("hiddenByGroup")) { //if item hiddenByGroup
502 | unHide($(this));
503 | }
504 | }
505 |
506 | if ($(this).parents('.noRenderGroup').length > 0) { // If nested in noRenderGroup
507 | if ($(this).children(".lidiv").children(".rendering").hasClass("render")) { //if item render
508 | noRender($(this), true);
509 | }
510 | }
511 | else { //sorted outside group
512 | if ($(this).children(".lidiv").children(".rendering").hasClass("noRenderByGroup")) { //if item noRenderByGroup
513 | render($(this));
514 | }
515 | }
516 |
517 | if($(this).hasClass("group")){
518 | $(this).removeClass("mjs-nestedSortable-leaf");
519 | $(this).addClass("mjs-nestedSortable-branch");
520 | $(this).addClass("mjs-nestedSortable-expanded");
521 | }
522 |
523 | $(this).removeClass("sorted");
524 | });
525 |
526 | skpCallback('skp:sortItem@');
527 | });
528 |
529 |
530 |
531 |
532 | ////////////// RENAME /////////////////
533 |
534 | //Detect double click, hide name, show input
535 | $(document).on('dblclick', '.stateNameText', function (e) {
536 | if ($(this).closest('div').parent().is('#state_0')) {} //Can't rename State0
537 | else {
538 | var name = $(this).text();
539 | $(this).closest('div').children(".stateName").hide();
540 | $(this).closest('div').children(".inputName").css('display', 'block').children(".inputNameInput").val(name).focus().select();
541 | }
542 | });
543 | //Select state when clicking (.selectable() messes with dblclick)
544 | $(document).on('click', '.stateNameText', function (e) {
545 | $(".ui-selected").each(function(e){
546 | $(this).removeClass("ui-selected");
547 | });
548 | $(this).closest('div').addClass("ui-selected");
549 | });
550 |
551 | //Detect enter key, trigger blur
552 | $(document).on('keyup', '.inputNameInput', function (e) {
553 | if ( e.keyCode === 13 ) { // 13 is enter key
554 | $(this).blur();
555 | }
556 | });
557 |
558 | //Detect esc key, fetch back the old name, trigger blur
559 | $(document).on('keyup', '.inputNameInput', function (e) {
560 | if ( e.keyCode === 27 ) { // 27 is esc key
561 | var name = $(this).closest('div').children(".stateName").children(".stateNameText").text();
562 | $(this).val(name);
563 | $(this).blur();
564 | }
565 | });
566 |
567 | //Detect blur, hide input and transfer name
568 | $(document).on('blur', '.inputNameInput', function (e) {
569 |
570 | var group = false;
571 | if ($(this).closest('div').parent().hasClass("group")) { //If group
572 | var group = true;
573 | }
574 |
575 | var oldname = $(this).closest('div').children(".stateName").children(".stateNameText").text();
576 | var stateName = $(this).val();
577 | if (group == false) { // If state
578 | stateID = $(this).closest('div').parent().attr('id');
579 | stateID = stateID.replace('state_', '');
580 | } else { // If group
581 | groupID = $(this).closest('div').parent().attr('id');
582 | groupID = groupID.replace('group_', '');
583 | }
584 |
585 | if(stateName.length == 0 ) { // Check if state name is empty
586 | alert('Invalid State Name.\n\nState name must have at least one character.');
587 | $(this).focus();
588 | }
589 |
590 | else if ( oldname == stateName ) { // Name doesn't change
591 | $(this).closest('span').hide();
592 | $(this).closest('div').children(".stateName").show();
593 | }
594 |
595 | else { // Name changes
596 | var stateName = $(this).val();
597 | $(this).closest('span').hide();
598 | $(this).closest('div').children(".stateName").show();
599 | $(this).closest('div').children(".stateName").children(".stateNameText").text(stateName);
600 | if (group == false) { // If state
601 | renameState(stateID, stateName); // Send rename to ruby
602 | } else { // If group
603 | renameGroup(groupID, stateName); // Send rename to ruby
604 | }
605 | }
606 | });
607 |
608 |
609 |
610 | ////////////// HEADER ////////////////
611 |
612 | $('#minimize').click(function () {
613 | var size = { "height":$(window).height(), "width":$(window).width() }; //Json
614 | var jsonSize = $.toJSON( size );
615 |
616 | if($(window).height() > 10){
617 | skpCallback('skp:minimizeDialog@' + jsonSize);
618 | }
619 | else {
620 | skpCallback('skp:maximizeDialog@' + $(window).width());
621 | }
622 | });
623 | $(window).resize(function(){
624 | if($(window).height() > 10){
625 | $('#minimize').css({height: '3px'});
626 | }
627 | else {
628 | $('#minimize').css({height: '10px'});
629 | }
630 | });
631 |
632 |
633 | ////////////// FOOTER ////////////////
634 |
635 | $('.footerElement').hover(function () {
636 | $(this).toggleClass('footerHover');
637 | }).mouseup(function(){
638 | $(this).removeClass('footerClick');
639 | }).mousedown(function(){
640 | $(this).addClass('footerClick');
641 | }).mouseleave(function(){
642 | $(this).removeClass('footerClick');
643 | });
644 |
645 |
646 | // Add a new State
647 | $('#newState').click(addState);
648 |
649 | // Add a new group
650 | $('#newGroup').click(addGroup);
651 |
652 | // Delete group or state
653 | $('#trash').click(trash);
654 |
655 | $('#update').click(function() {
656 | $(".ui-selected").each(function(e){
657 | stateID = $(this).parent().attr('id');
658 | stateID = stateID.replace('state_', '');
659 | skpCallback('skp:updateState@' + stateID);
660 | });
661 | });
662 |
663 |
664 | $('#print').click(function() {
665 | window.prompt("Copy to clipboard: Ctrl+C, Enter", $("#olsortable").html());
666 | });
667 |
668 |
669 | ////////////// UNDO REDO ////////////////
670 |
671 | $(document).keydown(function(e){
672 | if( e.which === 90 && (e.ctrlKey||e.metaKey) ){
673 | skpCallback('skp:undo');
674 | }
675 | });
676 |
677 | $(document).keydown(function(e){
678 | if( e.which === 89 && (e.ctrlKey||e.metaKey) ){
679 | skpCallback('skp:redo');
680 | }
681 | });
682 |
683 | ////////////// SHORTCUTS ////////////////
684 |
685 | $(document).keydown(function(e){ // CTRL A
686 | if( e.which === 65 && (e.ctrlKey||e.metaKey)){
687 | $(".lidiv").addClass("ui-selected");
688 | }
689 | });
690 |
691 | $(document).keydown(function(e){ // CTRL G
692 | if( e.which === 71 && (e.ctrlKey||e.metaKey) && !e.shiftKey ){
693 | groupStates();
694 | }
695 | });
696 |
697 | $(document).keydown(function(e){ // CTRL SHIFT G
698 | if( e.which === 71 && (e.ctrlKey||e.metaKey) && e.shiftKey ){
699 | unGroupStates();
700 | }
701 | });
702 |
703 | //////////////
704 |
705 | getCollapsedGroups();
706 |
707 | //////////////
708 |
709 | $(window).resize(function(){
710 | $('#statesContainer').css({
711 | height: $(window).height() - 55
712 | });
713 | });
714 | $(window).resize();
715 | });
--------------------------------------------------------------------------------