├── 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 |
More about this...
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 |
14 | Layers Panel history 15 |
Click on a date to go back to the corresponding ordering/sorting state.

16 | Most recent are on top.
17 | Orange dates are empty

18 |
19 |
20 |
21 | 22 | 23 | 67 | 68 | -------------------------------------------------------------------------------- /jbb_layers_panel/html/debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | Layer/group IDs 14 |
No issues detected with layer/group IDs.
15 |
Some layers/groups have duplicate IDs. Click here to fix it.
(You may need to click several times)

16 |
Current dictID :

17 |
18 |
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 |
13 | Startup 14 |
15 |
16 |
17 | Interface 18 |
19 |
20 | 21 |
22 |
23 | Behaviour 24 |
25 |
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 = '
  • ' + stateName + '
  • ' 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 = '
  • ' + groupName + '
    1. '; 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 | }); --------------------------------------------------------------------------------