From 8e06f41395c12df0019b883c9ac661c3078c82db Mon Sep 17 00:00:00 2001 From: Greg Priday Date: Thu, 11 Dec 2014 00:49:02 +0200 Subject: [PATCH] Initial commit of Page Builder. --- css/admin.css | 1697 ++++++++ css/admin.less | 1850 +++++++++ css/front.css | 49 + css/front.less | 26 + css/icons/readme.txt | 5 + css/icons/siteorigin-panels.eot | Bin 0 -> 2868 bytes css/icons/siteorigin-panels.svg | 18 + css/icons/siteorigin-panels.ttf | Bin 0 -> 2704 bytes css/icons/siteorigin-panels.woff | Bin 0 -> 2780 bytes css/images/cell-selected.png | Bin 0 -> 933 bytes css/images/cell-width.png | Bin 0 -> 975 bytes css/images/dialog-separator.png | Bin 0 -> 1342 bytes css/images/dialog-separator@2x.png | Bin 0 -> 1906 bytes css/images/dropdown-pointer.png | Bin 0 -> 1077 bytes css/images/tooltip-pointer.png | Bin 0 -> 963 bytes css/images/wpspin_light-2x.gif | Bin 0 -> 9097 bytes css/images/wpspin_light.gif | Bin 0 -> 2193 bytes css/mixins.less | 173 + inc/admin-actions.php | 183 + inc/css.php | 129 + inc/debug.php | 19 + inc/default-styles.php | 359 ++ inc/live-editor.php | 23 + inc/options.php | 179 + inc/plugin-activation.php | 131 + inc/revisions.php | 72 + inc/styles.php | 339 ++ inc/widgets-bundle.php | 64 + inc/widgets.php | 152 + js/siteorigin-panels-history.js | 301 ++ js/siteorigin-panels-live-editor.js | 308 ++ js/siteorigin-panels-styles.js | 169 + js/siteorigin-panels.js | 3281 ++++++++++++++++ js/styling.js | 45 + lang/siteorigin-panels.po | 827 ++++ license.txt | 674 ++++ readme.txt | 193 + siteorigin-panels.php | 1259 ++++++ tpl/admin-home-page.php | 39 + tpl/help.php | 15 + tpl/js-templates.php | 435 +++ tpl/metabox-panels.php | 8 + tpl/options.php | 97 + video/jplayer/Jplayer.swf | Bin 0 -> 14148 bytes video/jplayer/jquery.jplayer.min.js | 114 + video/jplayer/skins/premium/gui.php | 32 + .../jplayer/skins/premium/jplayer.premium.css | 188 + .../skins/premium/sprites/full-screen.png | Bin 0 -> 1216 bytes .../jplayer/skins/premium/sprites/handle.png | Bin 0 -> 1635 bytes .../skins/premium/sprites/large-play.png | Bin 0 -> 4492 bytes video/jplayer/skins/premium/sprites/pause.png | Bin 0 -> 1128 bytes video/jplayer/skins/premium/sprites/play.png | Bin 0 -> 1365 bytes video/jplayer/skins/premium/sprites/sep.png | Bin 0 -> 1039 bytes video/jplayer/skins/siteorigin/gui.php | 33 + .../skins/siteorigin/jplayer.siteorigin.css | 183 + .../skins/siteorigin/sprites/expand.png | Bin 0 -> 1053 bytes .../skins/siteorigin/sprites/large-play.png | Bin 0 -> 4492 bytes .../skins/siteorigin/sprites/pause.png | Bin 0 -> 1002 bytes .../jplayer/skins/siteorigin/sprites/play.png | Bin 0 -> 1265 bytes .../skins/siteorigin/sprites/restore.png | Bin 0 -> 1068 bytes .../skins/siteorigin/sprites/time-pointer.png | Bin 0 -> 965 bytes video/panels.video.jquery.js | 64 + video/poster.jpg | Bin 0 -> 59406 bytes widgets/basic.php | 439 +++ widgets/img/checks/black.png | Bin 0 -> 1144 bytes widgets/img/checks/blue.png | Bin 0 -> 1145 bytes widgets/img/checks/charcoal.png | Bin 0 -> 1153 bytes widgets/img/checks/green.png | Bin 0 -> 1144 bytes widgets/img/checks/light.png | Bin 0 -> 1153 bytes widgets/img/checks/orange.png | Bin 0 -> 1139 bytes widgets/img/checks/pink.png | Bin 0 -> 1139 bytes widgets/img/checks/purple.png | Bin 0 -> 1141 bytes widgets/img/checks/slate.png | Bin 0 -> 1153 bytes widgets/img/checks/tirquoise.png | Bin 0 -> 1140 bytes widgets/img/textures/dark-dashed.png | Bin 0 -> 3120 bytes widgets/img/textures/light-dashed.png | Bin 0 -> 4840 bytes widgets/js/admin.js | 2 + widgets/js/embedded-video.js | 3 + widgets/js/jquery.fitvids.js | 88 + widgets/less/functions.php | 93 + widgets/less/mixins.less | 183 + widgets/lib/color.php | 474 +++ widgets/lib/lessc.inc.php | 3473 +++++++++++++++++ widgets/widgets.php | 1042 +++++ .../widgets/animated-image/animated-image.php | 41 + widgets/widgets/animated-image/js/main.js | 42 + widgets/widgets/animated-image/js/onscreen.js | 24 + .../widgets/animated-image/tpl/default.php | 1 + widgets/widgets/button/button.php | 43 + widgets/widgets/button/presets/simple.php | 54 + widgets/widgets/button/styles/simple.css | 71 + widgets/widgets/button/styles/simple.less | 78 + widgets/widgets/button/tpl/simple.php | 3 + .../widgets/call-to-action/call-to-action.php | 39 + .../widgets/call-to-action/presets/simple.php | 24 + .../widgets/call-to-action/styles/simple.less | 70 + widgets/widgets/call-to-action/tpl/simple.php | 3 + widgets/widgets/list/list.php | 35 + widgets/widgets/list/presets/simple.php | 39 + widgets/widgets/list/styles/simple.less | 23 + widgets/widgets/list/tpl/simple.php | 5 + widgets/widgets/price-box/presets/simple.php | 8 + widgets/widgets/price-box/price-box.php | 52 + widgets/widgets/price-box/styles/simple.less | 38 + widgets/widgets/price-box/tpl/simple.php | 16 + .../widgets/testimonial/presets/simple.php | 6 + .../widgets/testimonial/styles/simple.less | 61 + widgets/widgets/testimonial/testimonial.php | 40 + widgets/widgets/testimonial/tpl/simple.php | 13 + 109 files changed, 20359 insertions(+) create mode 100644 css/admin.css create mode 100644 css/admin.less create mode 100644 css/front.css create mode 100644 css/front.less create mode 100644 css/icons/readme.txt create mode 100755 css/icons/siteorigin-panels.eot create mode 100755 css/icons/siteorigin-panels.svg create mode 100755 css/icons/siteorigin-panels.ttf create mode 100755 css/icons/siteorigin-panels.woff create mode 100644 css/images/cell-selected.png create mode 100644 css/images/cell-width.png create mode 100644 css/images/dialog-separator.png create mode 100644 css/images/dialog-separator@2x.png create mode 100644 css/images/dropdown-pointer.png create mode 100644 css/images/tooltip-pointer.png create mode 100644 css/images/wpspin_light-2x.gif create mode 100644 css/images/wpspin_light.gif create mode 100644 css/mixins.less create mode 100644 inc/admin-actions.php create mode 100644 inc/css.php create mode 100644 inc/debug.php create mode 100644 inc/default-styles.php create mode 100644 inc/live-editor.php create mode 100644 inc/options.php create mode 100644 inc/plugin-activation.php create mode 100644 inc/revisions.php create mode 100644 inc/styles.php create mode 100644 inc/widgets-bundle.php create mode 100644 inc/widgets.php create mode 100644 js/siteorigin-panels-history.js create mode 100644 js/siteorigin-panels-live-editor.js create mode 100644 js/siteorigin-panels-styles.js create mode 100644 js/siteorigin-panels.js create mode 100644 js/styling.js create mode 100644 lang/siteorigin-panels.po create mode 100644 license.txt create mode 100644 readme.txt create mode 100644 siteorigin-panels.php create mode 100644 tpl/admin-home-page.php create mode 100644 tpl/help.php create mode 100644 tpl/js-templates.php create mode 100644 tpl/metabox-panels.php create mode 100644 tpl/options.php create mode 100755 video/jplayer/Jplayer.swf create mode 100755 video/jplayer/jquery.jplayer.min.js create mode 100644 video/jplayer/skins/premium/gui.php create mode 100755 video/jplayer/skins/premium/jplayer.premium.css create mode 100644 video/jplayer/skins/premium/sprites/full-screen.png create mode 100644 video/jplayer/skins/premium/sprites/handle.png create mode 100644 video/jplayer/skins/premium/sprites/large-play.png create mode 100644 video/jplayer/skins/premium/sprites/pause.png create mode 100644 video/jplayer/skins/premium/sprites/play.png create mode 100644 video/jplayer/skins/premium/sprites/sep.png create mode 100644 video/jplayer/skins/siteorigin/gui.php create mode 100755 video/jplayer/skins/siteorigin/jplayer.siteorigin.css create mode 100644 video/jplayer/skins/siteorigin/sprites/expand.png create mode 100644 video/jplayer/skins/siteorigin/sprites/large-play.png create mode 100644 video/jplayer/skins/siteorigin/sprites/pause.png create mode 100644 video/jplayer/skins/siteorigin/sprites/play.png create mode 100644 video/jplayer/skins/siteorigin/sprites/restore.png create mode 100644 video/jplayer/skins/siteorigin/sprites/time-pointer.png create mode 100644 video/panels.video.jquery.js create mode 100644 video/poster.jpg create mode 100644 widgets/basic.php create mode 100644 widgets/img/checks/black.png create mode 100644 widgets/img/checks/blue.png create mode 100644 widgets/img/checks/charcoal.png create mode 100644 widgets/img/checks/green.png create mode 100644 widgets/img/checks/light.png create mode 100644 widgets/img/checks/orange.png create mode 100644 widgets/img/checks/pink.png create mode 100644 widgets/img/checks/purple.png create mode 100644 widgets/img/checks/slate.png create mode 100644 widgets/img/checks/tirquoise.png create mode 100644 widgets/img/textures/dark-dashed.png create mode 100644 widgets/img/textures/light-dashed.png create mode 100644 widgets/js/admin.js create mode 100644 widgets/js/embedded-video.js create mode 100755 widgets/js/jquery.fitvids.js create mode 100644 widgets/less/functions.php create mode 100755 widgets/less/mixins.less create mode 100644 widgets/lib/color.php create mode 100644 widgets/lib/lessc.inc.php create mode 100644 widgets/widgets.php create mode 100644 widgets/widgets/animated-image/animated-image.php create mode 100644 widgets/widgets/animated-image/js/main.js create mode 100644 widgets/widgets/animated-image/js/onscreen.js create mode 100644 widgets/widgets/animated-image/tpl/default.php create mode 100644 widgets/widgets/button/button.php create mode 100644 widgets/widgets/button/presets/simple.php create mode 100644 widgets/widgets/button/styles/simple.css create mode 100644 widgets/widgets/button/styles/simple.less create mode 100644 widgets/widgets/button/tpl/simple.php create mode 100644 widgets/widgets/call-to-action/call-to-action.php create mode 100644 widgets/widgets/call-to-action/presets/simple.php create mode 100644 widgets/widgets/call-to-action/styles/simple.less create mode 100644 widgets/widgets/call-to-action/tpl/simple.php create mode 100644 widgets/widgets/list/list.php create mode 100644 widgets/widgets/list/presets/simple.php create mode 100644 widgets/widgets/list/styles/simple.less create mode 100644 widgets/widgets/list/tpl/simple.php create mode 100644 widgets/widgets/price-box/presets/simple.php create mode 100644 widgets/widgets/price-box/price-box.php create mode 100644 widgets/widgets/price-box/styles/simple.less create mode 100644 widgets/widgets/price-box/tpl/simple.php create mode 100644 widgets/widgets/testimonial/presets/simple.php create mode 100644 widgets/widgets/testimonial/styles/simple.less create mode 100644 widgets/widgets/testimonial/testimonial.php create mode 100644 widgets/widgets/testimonial/tpl/simple.php diff --git a/css/admin.css b/css/admin.css new file mode 100644 index 000000000..43d74946e --- /dev/null +++ b/css/admin.css @@ -0,0 +1,1697 @@ +@font-face { + font-family: 'siteorigin-panels'; + src: url('icons/siteorigin-panels.eot?-yv2c11'); + src: url('icons/siteorigin-panels.eot?#iefix-yv2c11') format('embedded-opentype'), url('icons/siteorigin-panels.woff?-yv2c11') format('woff'), url('icons/siteorigin-panels.ttf?-yv2c11') format('truetype'), url('icons/siteorigin-panels.svg?-yv2c11#siteorigin-panels') format('svg'); + font-weight: normal; + font-style: normal; +} +/* This is for the metabox */ +#so-panels-panels.attached-to-editor { + margin-top: 20px; +} +#so-panels-panels.attached-to-editor h3.hndle { + display: none; +} +#so-panels-panels.attached-to-editor .inside { + margin: 0 !important; + padding: 0 !important; +} +#so-panels-panels.attached-to-editor .so-toolbar .so-switch-to-standard { + display: block; +} +/* Everything for the main builder interface */ +.siteorigin-panels-builder { + position: relative; + /* These are generic iconic buttons used in the page builder interface */ + /* Page Builder icons */ +} +.siteorigin-panels-builder .so-builder-container { + /* Top padding for the toolbar */ + padding-top: 38px; + position: relative; +} +.siteorigin-panels-builder .so-tool-button { + padding: 6px 7px; + font-size: 11px; + text-decoration: none; + line-height: 0.9em; + float: left; + margin-right: 2px; + display: block; + visibility: visible; + position: relative; + border: 1px solid #C0C0C0; + background: #f0f0f0; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #f0f0f0), color-stop(1, #f9f9f9)); + background: -ms-linear-gradient(bottom, #f0f0f0, #f9f9f9); + background: -moz-linear-gradient(center bottom, #f0f0f0 0%, #f9f9f9 100%); + background: -o-linear-gradient(#f9f9f9, #f0f0f0); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f9f9f9', endColorstr='#f0f0f0', GradientType=0); + -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.04); + -moz-box-shadow: 0 1px 1px rgba(0,0,0,0.04); + box-shadow: 0 1px 1px rgba(0,0,0,0.04); + outline: none; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; +} +.siteorigin-panels-builder .so-tool-button span { + display: inline-block; + color: #666666; + text-shadow: 0 1px 0 #FFFFFF; + min-width: 10px; + text-align: center; +} +.siteorigin-panels-builder .so-tool-button:hover { + background: #FFFFFF; +} +.siteorigin-panels-builder .so-tool-button:hover span { + color: #444444; +} +.siteorigin-panels-builder .so-builder-toolbar { + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + border-bottom: 1px solid #D0D0D0; + background: #F5F5F5; + line-height: 1em; + position: absolute; + z-index: 101; + white-space: nowrap; + overflow-x: hidden; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04); + top: 0; + left: 0; + width: 100%; + padding: 6px 9px; + margin-top: 0 !important; + zoom: 1; + /* The toolbar buttons */ +} +.siteorigin-panels-builder .so-builder-toolbar:before { + content: ''; + display: block; +} +.siteorigin-panels-builder .so-builder-toolbar:after { + content: ''; + display: table; + clear: both; +} +.siteorigin-panels-builder .so-builder-toolbar > .so-tool-button { + display: inline-block; + color: #666666; + padding-right: 8px; +} +.siteorigin-panels-builder .so-builder-toolbar > .so-tool-button .so-panels-icon { + float: left; + margin: 0 5px 0 0; +} +.siteorigin-panels-builder .so-builder-toolbar > .so-tool-button:hover { + color: #444444; +} +.siteorigin-panels-builder .so-builder-toolbar .so-switch-to-standard { + position: absolute; + top: 5px; + right: 10px; + display: none; + text-decoration: none; + color: #666666; + padding: 5px 6px; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + border: 1px solid transparent; + margin-top: 2px; + font-size: 11px; +} +.siteorigin-panels-builder .so-builder-toolbar .so-switch-to-standard:hover { + background: #fafafa; + border: 1px solid #999999; + color: #444444; +} +@media screen and (max-width: 600px) { + .siteorigin-panels-builder .so-builder-toolbar { + padding: 10px; + } + .siteorigin-panels-builder .so-builder-toolbar > .so-tool-button { + padding-right: 2px; + } + .siteorigin-panels-builder .so-builder-toolbar > .so-tool-button .so-panels-icon { + font-size: 20px; + } + .siteorigin-panels-builder .so-builder-toolbar > .so-tool-button span.so-button-text { + display: none; + } +} +.siteorigin-panels-builder .so-rows-container { + padding: 20px 15px 0 15px; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar { + zoom: 1; + margin-bottom: 4px; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar:before { + content: ''; + display: block; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar:after { + content: ''; + display: table; + clear: both; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-tool-button { + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 22px; + padding: 5px; + font-size: 10px; + float: right; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-tool-button.so-row-move { + cursor: move; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper { + position: relative; + float: right; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper .so-dropdown-links-wrapper { + display: none; + z-index: 11; + position: absolute; + right: -10px; + padding: 6px 0 0 0; + top: 22px; + width: 125px; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper .so-dropdown-links-wrapper ul { + margin: 0; + border: 1px solid #C0C0C0; + background: #F9F9F9; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + padding: 4px 0; + -webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.1); + -moz-box-shadow: 0 2px 2px rgba(0,0,0,0.1); + box-shadow: 0 2px 2px rgba(0,0,0,0.1); +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper .so-dropdown-links-wrapper ul li { + margin: 0; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper .so-dropdown-links-wrapper ul li:first-child { + border-top-width: 1px; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper .so-dropdown-links-wrapper ul li a { + display: block; + padding: 2px 8px; + text-decoration: none; + color: #666; + font-size: 11px; + font-weight: bold; + outline: 0 !important; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + /* Specific drop down hovers */ +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper .so-dropdown-links-wrapper ul li a:hover { + background: #F0F0F0; + color: #444; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper .so-dropdown-links-wrapper ul li a .dashicons { + font-size: 16px; + margin: 0; + float: right; + line-height: 16px; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper .so-dropdown-links-wrapper ul li a.so-row-delete { + color: #a00; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper .so-dropdown-links-wrapper ul li a.so-row-delete:hover { + color: #FFF; + background: #a00; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper .so-dropdown-links-wrapper ul .so-pointer { + width: 12px; + height: 6px; + position: absolute; + z-index: 12; + background: url("images/dropdown-pointer.png"); + background-size: 12px 6px; + top: 1px; + right: 18px; +} +.siteorigin-panels-builder .so-rows-container .so-row-toolbar .so-dropdown-wrapper:hover .so-dropdown-links-wrapper { + display: block; +} +.siteorigin-panels-builder .so-rows-container .ui-sortable-placeholder { + visibility: visible !important; + background: #F7F7F7; + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +.siteorigin-panels-builder .so-rows-container .so-row-container { + margin-bottom: 20px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells { + zoom: 1; + margin: 0 -5px; + position: relative; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells:before { + content: ''; + display: block; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells:after { + content: ''; + display: table; + clear: both; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .ui-resizable-handle.ui-resizable-w { + width: 10px; + left: -11px; + cursor: col-resize; + background: rgba(0, 150, 211, 0); + background: rgba(0, 150, 211, 0.25); + -webkit-transition: background 0.25s ease-in-out; + -moz-transition: background 0.25s ease-in-out; + -o-transition: background 0.25s ease-in-out; + transition: background 0.25s ease-in-out; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .ui-resizable-handle.ui-resizable-w:hover { + background: rgba(0, 150, 211, 0.1); +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell { + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + float: left; + position: relative; + padding: 0 5px; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell.so-first { + margin-left: 0; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell.so-last { + margin-right: 0; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .cell-wrapper { + background: #e4eff4; + border: 1px solid #bcccd2; + padding: 10px 10px 4px 10px; + /* 6px bottom to remove bottom margin from panels */ + height: 100%; + min-height: 70px; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell.cell-selected .cell-wrapper { + background: #cae7f4 url("images/cell-selected.png") repeat; + border-color: #9abcc7; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.2); +} +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + .siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell.cell-selected .cell-wrapper { + background-size: 3px 3px; + } +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell, +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .cell-wrapper { + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget { + cursor: move; + margin-bottom: 5px; + background: #f9f9fb; + border: 1px solid #9bafb5; + max-height: 49px; + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.075); + -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.075); + box-shadow: 0 1px 2px rgba(0,0,0,0.075); +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget:hover { + border: 1px solid #93a7ad; + background: #feffff; + -webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.075); + -moz-box-shadow: 0 2px 2px rgba(0,0,0,0.075); + box-shadow: 0 2px 2px rgba(0,0,0,0.075); +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget .so-widget-wrapper { + padding: 7px 9px; + overflow: hidden; + position: relative; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget h4 { + display: block; + cursor: pointer; + margin: 0 15px 3px 0; + font-weight: 600; + line-height: 1.25em; + color: #474747; + text-shadow: 0 1px 0 #FFF; + white-space: nowrap; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget h4 span { + font-weight: normal; + display: inline-block; + color: #999; + text-shadow: 0 1px 0 #FFF; + margin-left: 12px; + margin-right: 5px; + font-style: italic; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget .title .actions { + font-size: 12px; + position: absolute; + top: 5px; + right: 7px; + cursor: pointer; + padding: 2px 2px 2px 15px; + z-index: 10; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget .title .actions:hover { + background: #feffff; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget .title .actions:hover a { + opacity: 1; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget .title .actions a { + display: none; + margin-right: 3px; + text-decoration: none; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget .title .actions a.widget-delete { + color: #FF0000; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget .title .actions a.widget-delete:hover { + color: white; + background: #FF0000; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget:hover .title a { + display: inline-block; + opacity: 0.5; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget.panel-being-dragged .title .actions { + display: none; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget small { + display: block; + height: 16px; + overflow: hidden; + color: #777; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget .form { + display: none; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget.widget-being-dragged { + opacity: 0.9; + pointer-events: none; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .widgets-container .so-widget-sortable-highlight { + border: 1px solid; + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 49px; + background: #ddebef; + border-color: #bcccd2; + margin-bottom: 5px; + position: relative; + -webkit-box-shadow: inset 2px 2px 2px rgba(0,0,0,0.01); + -moz-box-shadow: inset 2px 2px 2px rgba(0,0,0,0.01); + box-shadow: inset 2px 2px 2px rgba(0,0,0,0.01); +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .resize-handle { + z-index: 100; + position: absolute; + top: 0; + width: 10px; + left: -5px; + cursor: col-resize; + background: #e5f4fa; + height: 100%; + -webkit-transition: background 0.25s ease-in-out; + -moz-transition: background 0.25s ease-in-out; + -o-transition: background 0.25s ease-in-out; + transition: background 0.25s ease-in-out; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell .resize-handle:hover { + background: #bfe4f3; +} +.siteorigin-panels-builder .so-rows-container .so-row-container .so-cells .cell:first-child .resize-handle { + display: none; +} +.siteorigin-panels-builder .so-panels-icon { + font-family: 'siteorigin-panels'; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.siteorigin-panels-builder .so-panels-icon.so-panels-icon-plus:before { + content: "\f067"; +} +.siteorigin-panels-builder .so-panels-icon.so-panels-icon-eye:before { + content: "\f06e"; +} +.siteorigin-panels-builder .so-panels-icon.so-panels-icon-arrows-v:before { + content: "\f07d"; +} +.siteorigin-panels-builder .so-panels-icon.so-panels-icon-wrench:before { + content: "\f0ad"; +} +.siteorigin-panels-builder .so-panels-icon.so-panels-icon-columns:before { + content: "\f0db"; +} +.siteorigin-panels-builder .so-panels-icon.so-panels-icon-rotate-left:before { + content: "\f0e2"; +} +.siteorigin-panels-builder .so-panels-icon.so-panels-icon-puzzle-piece:before { + content: "\f12e"; +} +.siteorigin-panels-builder .so-panels-icon.so-panels-icon-cubes:before { + content: "\f1b3"; +} +.siteorigin-panels-builder .so-panels-welcome-message { + text-align: center; + padding: 0px 15px 20px 15px; + color: #555; + line-height: 1.8em; +} +.siteorigin-panels-builder .so-panels-welcome-message .so-message-wrapper { + padding: 15px 10px; + background: #F8F8F8; + border: 1px solid #E0E0E0; +} +.siteorigin-panels-builder .so-panels-welcome-message .so-tool-button { + font-size: inherit; + display: inline-block; + float: none; + color: #666; + padding: 5px 10px; + margin: 0 3px; +} +.siteorigin-panels-builder .so-panels-welcome-message .so-tool-button .so-panels-icon { + color: #777; + font-size: 0.8em; +} +/* This is to display a draggable widget */ +.so-widget.ui-sortable-helper.widget-being-dragged { + cursor: move; + margin-bottom: 5px; + background: #f9f9fb; + border: 1px solid #9bafb5; + max-height: 49px; + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.075); + -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.075); + box-shadow: 0 1px 2px rgba(0,0,0,0.075); +} +.so-widget.ui-sortable-helper.widget-being-dragged:hover { + border: 1px solid #93a7ad; + background: #feffff; + -webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.075); + -moz-box-shadow: 0 2px 2px rgba(0,0,0,0.075); + box-shadow: 0 2px 2px rgba(0,0,0,0.075); +} +.so-widget.ui-sortable-helper.widget-being-dragged .so-widget-wrapper { + padding: 7px 9px; + overflow: hidden; + position: relative; +} +.so-widget.ui-sortable-helper.widget-being-dragged h4 { + display: block; + cursor: pointer; + margin: 0 15px 3px 0; + font-weight: 600; + line-height: 1.25em; + color: #474747; + text-shadow: 0 1px 0 #FFF; + white-space: nowrap; +} +.so-widget.ui-sortable-helper.widget-being-dragged h4 span { + font-weight: normal; + display: inline-block; + color: #999; + text-shadow: 0 1px 0 #FFF; + margin-left: 12px; + margin-right: 5px; + font-style: italic; +} +.so-widget.ui-sortable-helper.widget-being-dragged .title .actions { + font-size: 12px; + position: absolute; + top: 5px; + right: 7px; + cursor: pointer; + padding: 2px 2px 2px 15px; + z-index: 10; +} +.so-widget.ui-sortable-helper.widget-being-dragged .title .actions:hover { + background: #feffff; +} +.so-widget.ui-sortable-helper.widget-being-dragged .title .actions:hover a { + opacity: 1; +} +.so-widget.ui-sortable-helper.widget-being-dragged .title .actions a { + display: none; + margin-right: 3px; + text-decoration: none; +} +.so-widget.ui-sortable-helper.widget-being-dragged .title .actions a.widget-delete { + color: #FF0000; +} +.so-widget.ui-sortable-helper.widget-being-dragged .title .actions a.widget-delete:hover { + color: white; + background: #FF0000; +} +.so-widget.ui-sortable-helper.widget-being-dragged:hover .title a { + display: inline-block; + opacity: 0.5; +} +.so-widget.ui-sortable-helper.widget-being-dragged.panel-being-dragged .title .actions { + display: none; +} +.so-widget.ui-sortable-helper.widget-being-dragged small { + display: block; + height: 16px; + overflow: hidden; + color: #777; +} +.so-widget.ui-sortable-helper.widget-being-dragged .form { + display: none; +} +.so-widget.ui-sortable-helper.widget-being-dragged.widget-being-dragged { + opacity: 0.9; + pointer-events: none; +} +/* Handles displaying a builder in the WordPress widget interface */ +.widgets-holder-wrap .widget-inside .siteorigin-panels-builder .so-builder-container { + padding-top: 0; +} +.widgets-holder-wrap .widget-inside .siteorigin-panels-builder .so-rows-container { + padding: 10px 0 0 0; +} +.widgets-holder-wrap .widget-inside .siteorigin-panels-builder .so-builder-toolbar { + padding-left: 15px; + padding-right: 15px; + margin: 0 -15px; +} +.so-panels-dialog { + /* The add widget dialog */ + /* The row edit dialog */ + /* For prebuilt layouts */ + /* Everything we need for the style fields */ + /* Special case of the builder interface being inside a dialog */ +} +.so-panels-dialog .so-overlay, +.so-panels-dialog .so-content, +.so-panels-dialog .so-title-bar, +.so-panels-dialog .so-toolbar, +.so-panels-dialog .so-left-sidebar, +.so-panels-dialog .so-right-sidebar { + z-index: 100000; + position: fixed; + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 15px; +} +.so-panels-dialog .so-content, +.so-panels-dialog .so-left-sidebar, +.so-panels-dialog .so-right-sidebar { + overflow-y: auto; +} +.so-panels-dialog .so-overlay { + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); +} +.so-panels-dialog .so-content { + top: 80px; + left: 30px; + right: 30px; + bottom: 88px; + background-color: #fdfdfd; + overflow-x: hidden; + -webkit-box-shadow: inset 0 2px 2px rgba(0,0,0,0.03); + -moz-box-shadow: inset 0 2px 2px rgba(0,0,0,0.03); + box-shadow: inset 0 2px 2px rgba(0,0,0,0.03); +} +.so-panels-dialog .so-content > *:first-child { + margin-top: 0; +} +.so-panels-dialog .so-content > *:last-child { + margin-bottom: 0; +} +.so-panels-dialog .so-content .so-content-tabs > * { + display: none; +} +.so-panels-dialog .so-title-bar { + left: 30px; + right: 30px; + top: 30px; + height: 50px; + background-color: #fafafa; + border-bottom: 1px solid #d8d8d8; + /* These are the action buttons in the title bar */ +} +.so-panels-dialog .so-title-bar h3.so-title { + margin: 0 !important; + padding: 0 !important; +} +.so-panels-dialog .so-title-bar h3.so-parent-link { + cursor: pointer; + position: relative; + float: left; + margin: 0 15px 0 0 !important; + padding: 0 27px 0 0 !important; +} +.so-panels-dialog .so-title-bar h3.so-parent-link .so-separator { + position: absolute; + top: -15px; + right: 0; + width: 12px; + height: 50px; + display: block; + background: url(images/dialog-separator.png) no-repeat; +} +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + .so-panels-dialog .so-title-bar h3.so-parent-link .so-separator { + background: url(images/dialog-separator@2x.png) no-repeat; + background-size: cover; + } +} +.so-panels-dialog .so-title-bar a { + position: absolute; + box-sizing: border-box; + width: 50px; + height: 50px; + display: block; + top: 0; + right: 0; + -webkit-transition: all 0.2s ease; + -moz-transition: all 0.2s ease; + -o-transition: all 0.2s ease; + transition: all 0.2s ease; + background: #fafafa; + border-left: 1px solid #d8d8d8; + border-bottom: 1px solid #d8d8d8; + /* Disabled nav */ +} +.so-panels-dialog .so-title-bar a:hover { + background: #e9e9e9; +} +.so-panels-dialog .so-title-bar a:hover .so-dialog-icon { + color: #333333; +} +.so-panels-dialog .so-title-bar a .so-dialog-icon { + position: absolute; + top: 50%; + left: 50%; + text-decoration: none; + width: 20px; + height: 20px; + margin-left: -10px; + margin-top: -10px; + color: #666666; + text-align: center; +} +.so-panels-dialog .so-title-bar a .so-dialog-icon:before { + font: 400 20px/1em dashicons; + top: 7px; + left: 13px; +} +.so-panels-dialog .so-title-bar a.so-close { + right: 0; +} +.so-panels-dialog .so-title-bar a.so-close .so-dialog-icon:before { + content: "\f335"; +} +.so-panels-dialog .so-title-bar a.so-next { + right: 50px; +} +.so-panels-dialog .so-title-bar a.so-next .so-dialog-icon:before { + content: '\f345'; +} +.so-panels-dialog .so-title-bar a.so-previous { + right: 100px; +} +.so-panels-dialog .so-title-bar a.so-previous .so-dialog-icon:before { + content: '\f341'; +} +.so-panels-dialog .so-title-bar a.so-nav.so-disabled { + cursor: default; + pointer-events: none; +} +.so-panels-dialog .so-title-bar a.so-nav.so-disabled .so-dialog-icon { + color: #dddddd; +} +.so-panels-dialog .so-toolbar { + left: 30px; + right: 30px; + bottom: 30px; + height: 58px; + background-color: #fafafa; + border-top: 1px solid #d8d8d8; +} +.so-panels-dialog .so-toolbar .so-status { + float: left; + padding-top: 6px; + padding-bottom: 6px; + font-style: italic; + color: #999999; + line-height: 1em; +} +.so-panels-dialog .so-toolbar .so-status.so-panels-loading { + padding-left: 26px; + background-position: left center; +} +.so-panels-dialog .so-toolbar .so-buttons { + float: right; +} +.so-panels-dialog .so-toolbar .so-buttons .action-buttons { + position: absolute; + left: 15px; + top: 50%; + margin-top: -0.65em; +} +.so-panels-dialog .so-toolbar .so-buttons .action-buttons a { + display: inline; + padding: 0.2em 0.5em; + line-height: 1em; + margin-right: 0.5em; + text-decoration: none; +} +.so-panels-dialog .so-toolbar .so-buttons .action-buttons .so-delete { + color: #a00; +} +.so-panels-dialog .so-toolbar .so-buttons .action-buttons .so-delete:hover { + background: #a00; + color: #FFFFFF; +} +.so-panels-dialog .so-toolbar .so-buttons .action-buttons .so-duplicate:hover { + text-decoration: underline; +} +.so-panels-dialog .so-left-sidebar, +.so-panels-dialog .so-right-sidebar { + background-color: #f3f3f3; +} +.so-panels-dialog .so-left-sidebar { + display: none; + top: 30px; + left: 30px; + bottom: 30px; + width: 290px; + border-right: 1px solid #d8d8d8; +} +.so-panels-dialog .so-left-sidebar h4 { + margin: 0 0 20px 0; + font-size: 18px; +} +.so-panels-dialog .so-left-sidebar .so-sidebar-search { + width: 100%; + padding: 6px; + margin-bottom: 20px; +} +.so-panels-dialog .so-left-sidebar .so-sidebar-tabs { + list-style: none; + margin: 0 -15px; +} +.so-panels-dialog .so-left-sidebar .so-sidebar-tabs li { + margin-bottom: 0; +} +.so-panels-dialog .so-left-sidebar .so-sidebar-tabs li a { + padding: 7px 16px; + display: block; + font-size: 14px; + text-decoration: none; + box-shadow: none !important; +} +.so-panels-dialog .so-left-sidebar .so-sidebar-tabs li a:hover { + background: #FFFFFF; +} +.so-panels-dialog .so-left-sidebar .so-sidebar-tabs li.tab-active a { + color: #555; + font-weight: bold; + background: #FFFFFF; +} +.so-panels-dialog .so-left-sidebar .so-sidebar-tabs li.tab-active a:hover { + background: #FFFFFF; +} +.so-panels-dialog .so-right-sidebar { + display: none; + top: 80px; + right: 30px; + bottom: 88px; + width: 290px; + border-left: 1px solid #d8d8d8; +} +.so-panels-dialog .so-right-sidebar h3 { + color: #333; +} +.so-panels-dialog .so-right-sidebar h3:first-child { + margin-top: 0; +} +.so-panels-dialog .so-sidebar .form-field { + margin-bottom: 20px; +} +.so-panels-dialog .so-sidebar .form-field label { + font-weight: 500; + font-size: 15px; + display: block; + margin-bottom: 10px; +} +.so-panels-dialog.so-panels-dialog-has-left-sidebar .so-content, +.so-panels-dialog.so-panels-dialog-has-left-sidebar .so-toolbar, +.so-panels-dialog.so-panels-dialog-has-left-sidebar .so-title-bar { + left: 320px; +} +.so-panels-dialog.so-panels-dialog-has-left-sidebar .so-content { + -webkit-box-shadow: inset 2px 2px 2px rgba(0,0,0,0.03); + -moz-box-shadow: inset 2px 2px 2px rgba(0,0,0,0.03); + box-shadow: inset 2px 2px 2px rgba(0,0,0,0.03); +} +.so-panels-dialog.so-panels-dialog-has-left-sidebar .so-left-sidebar { + display: block; +} +.so-panels-dialog.so-panels-dialog-has-right-sidebar .so-content { + right: 320px; +} +.so-panels-dialog.so-panels-dialog-has-right-sidebar .so-right-sidebar { + display: block; +} +.so-panels-dialog.so-panels-dialog-edit-widget .so-left-sidebar .so-widgets .so-widget { + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + border: 1px solid #cccccc; + cursor: pointer; + padding: 10px; + background: #f9f9f9; + -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.075), inset 0 1px 0 #FFFFFF; + -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.075), inset 0 1px 0 #FFFFFF; + box-shadow: 0 1px 2px rgba(0,0,0,0.075), inset 0 1px 0 #FFFFFF; + margin-bottom: 15px; +} +.so-panels-dialog.so-panels-dialog-edit-widget .so-left-sidebar .so-widgets .so-widget:hover { + border: 1px solid #BBBBBB; + background: #FFFFFF; +} +.so-panels-dialog.so-panels-dialog-edit-widget .so-left-sidebar .so-widgets .so-widget.so-current { + border-color: #0074a2; + background: #2ea2cc; + cursor: auto; + -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.2); + -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.2); + box-shadow: 0 1px 2px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.2); +} +.so-panels-dialog.so-panels-dialog-edit-widget .so-left-sidebar .so-widgets .so-widget.so-current h3 { + color: #FFFFFF; +} +.so-panels-dialog.so-panels-dialog-edit-widget .so-left-sidebar .so-widgets .so-widget.so-current small { + color: #eeeeee; +} +.so-panels-dialog.so-panels-dialog-edit-widget .so-left-sidebar .so-widgets .so-widget.so-current:hover { + border-color: #0074a2; + background: #2ea2cc; +} +.so-panels-dialog.so-panels-dialog-edit-widget .so-left-sidebar .so-widgets .so-widget:last-child { + margin-bottom: 0; +} +.so-panels-dialog.so-panels-dialog-edit-widget .so-left-sidebar .so-widgets .so-widget h3 { + margin: 0 0 7px 0; + padding: 0; + height: 1.2em; + color: #222222; + font-size: 14px; +} +.so-panels-dialog.so-panels-dialog-edit-widget .so-left-sidebar .so-widgets .so-widget small { + font-size: 11px; + line-height: 1.25em; + display: block; + overflow: hidden; + color: #888888; +} +.so-panels-dialog.so-panels-dialog-add-widget .widget-type-list { + zoom: 1; + margin: 0 -5px -10px -5px; + min-height: 10px; +} +.so-panels-dialog.so-panels-dialog-add-widget .widget-type-list:before { + content: ''; + display: block; +} +.so-panels-dialog.so-panels-dialog-add-widget .widget-type-list:after { + content: ''; + display: table; + clear: both; +} +.so-panels-dialog.so-panels-dialog-add-widget .widget-type-list .widget-type { + -ms-user-select: none; + /* IE 10+ */ + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; + user-select: none; + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + width: 25%; + padding: 0 5px; + margin-bottom: 10px; + float: left; +} +@media (max-width: 1280px) { + .so-panels-dialog.so-panels-dialog-add-widget .widget-type-list .widget-type { + width: 33.333%; + } +} +@media (max-width: 960px) { + .so-panels-dialog.so-panels-dialog-add-widget .widget-type-list .widget-type { + width: 50%; + } +} +.so-panels-dialog.so-panels-dialog-add-widget .widget-type-list .widget-type h3 { + margin: 0 0 7px 0; + padding: 0; + height: 1.2em; + color: #222222; + font-size: 14px; +} +.so-panels-dialog.so-panels-dialog-add-widget .widget-type-list .widget-type small { + font-size: 11px; + height: 2.5em; + line-height: 1.25em; + display: block; + overflow: hidden; + color: #888888; +} +.so-panels-dialog.so-panels-dialog-add-widget .widget-type-list .widget-type .widget-icon { + font-size: 20px; + width: 20px; + height: 20px; + color: #666; + float: left; + margin: -1px 0.5em 0 0; +} +.so-panels-dialog.so-panels-dialog-add-widget .widget-type-list .widget-type-wrapper { + border: 1px solid #cccccc; + cursor: pointer; + padding: 10px; + background: #F8F8F8; + -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.075); + -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.075); + box-shadow: 0 1px 2px rgba(0,0,0,0.075); +} +.so-panels-dialog.so-panels-dialog-add-widget .widget-type-list .widget-type-wrapper:hover { + border: 1px solid #BBBBBB; + background: #FFFFFF; + -webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.075); + -moz-box-shadow: 0 2px 2px rgba(0,0,0,0.075); + box-shadow: 0 2px 2px rgba(0,0,0,0.075); +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form { + zoom: 1; + padding: 8px; + border: 1px solid #ccc; + margin-bottom: 20px; + background: #F3F3F3; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form:before { + content: ''; + display: block; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form:after { + content: ''; + display: table; + clear: both; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form input, +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form select, +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form button, +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form strong, +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form span { + display: block; + float: left; + margin-right: 5px; + outline: none; + box-shadow: none; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form strong, +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form span { + margin-top: 5px; + margin-right: 0.75em; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form span { + margin-left: 0.75em; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-set-form label { + display: inline; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview { + margin: 0 -6px; + height: 360px; + position: relative; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell, +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell-in, +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell-weight { + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell { + float: left; + position: relative; + padding: 0 6px; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell .preview-cell-in { + border: 1px solid #bcccd2; + min-height: 360px; + background: #e4eff4; + position: relative; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell .preview-cell-in .preview-cell-weight, +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell .preview-cell-in .preview-cell-weight-input { + position: absolute; + font-size: 17px; + font-weight: bold; + top: 50%; + left: 50%; + width: 80px; + text-align: center; + color: #5e6d72; + margin: -0.95em 0 0 -40px; + padding: 10px 0; + border: 1px solid transparent; + line-height: 1.4em !important; + overflow: hidden; + cursor: pointer; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell .preview-cell-in .preview-cell-weight:after, +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell .preview-cell-in .preview-cell-weight-input:after { + content: '%'; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell .preview-cell-in .preview-cell-weight:hover, +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell .preview-cell-in .preview-cell-weight-input:hover { + background: #F6F6F6; + border: 1px solid #D0D0D0; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell .preview-cell-in .preview-cell-weight-input { + background: #F6F6F6; + border: 1px solid #D0D0D0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell .resize-handle { + z-index: 100; + position: absolute; + top: 0; + width: 12px; + left: -6px; + cursor: col-resize; + background: #e5f4fb; + height: 360px; + -webkit-transition: background 0.15s ease-in-out; + -moz-transition: background 0.15s ease-in-out; + -o-transition: background 0.15s ease-in-out; + transition: background 0.15s ease-in-out; +} +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell .resize-handle:hover, +.so-panels-dialog.so-panels-dialog-row-edit .so-content .row-preview .preview-cell .resize-handle.ui-draggable-dragging { + background: #b7e0f1; +} +.so-panels-dialog.so-panels-dialog-history .so-left-sidebar { + padding: 0; +} +.so-panels-dialog.so-panels-dialog-history .history-entries .history-entry { + padding: 10px; + background: #F8F8F8; + cursor: pointer; + border-bottom: 1px solid #ccc; +} +.so-panels-dialog.so-panels-dialog-history .history-entries .history-entry h3 { + margin: 0 0 0.6em 0; + font-size: 12px; + font-weight: bold; + color: #444444; + line-height: 1em; +} +.so-panels-dialog.so-panels-dialog-history .history-entries .history-entry .timesince { + color: #999999; + font-size: 11px; + line-height: 1em; +} +.so-panels-dialog.so-panels-dialog-history .history-entries .history-entry:hover { + background: #F0F0F0; +} +.so-panels-dialog.so-panels-dialog-history .history-entries .history-entry.so-selected { + background: #EEEEEE; +} +.so-panels-dialog.so-panels-dialog-history .history-entries .history-entry .count { + color: #999999; +} +.so-panels-dialog.so-panels-dialog-history .so-content { + padding: 0; + overflow-y: hidden; +} +.so-panels-dialog.so-panels-dialog-history .so-content form.history-form { + display: none; +} +.so-panels-dialog.so-panels-dialog-history .so-content iframe.siteorigin-panels-history-iframe { + width: 100%; + height: 100%; +} +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content { + padding-left: 10px; + padding-right: 10px; +} +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content .layout { + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + float: left; + width: 33.333%; + padding: 0 5px 10px 5px; +} +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content .layout .layout-inside { + padding: 20px; + cursor: pointer; + border: 1px solid #cccccc; + background: #F8F8F8; + -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.075); + -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.075); + box-shadow: 0 1px 2px rgba(0,0,0,0.075); +} +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content .layout .layout-inside:hover { + border: 1px solid #BBBBBB; + background: #FFFFFF; + -webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.075); + -moz-box-shadow: 0 2px 2px rgba(0,0,0,0.075); + box-shadow: 0 2px 2px rgba(0,0,0,0.075); +} +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content .layout .layout-inside h4 { + font-size: 15px; + margin: 0; + line-height: 1.2em; + height: 1.2em; + overflow: hidden; +} +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content .layout .layout-inside .description { + line-height: 1.2em; + height: 1.2em; + margin-top: 0.7em; + font-size: 12px; + color: #888; + overflow: hidden; +} +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content .layout .layout-inside .dashicons { + display: none; + float: left; + margin-top: 10px; +} +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content .layout.so-selected .layout-inside { + border: 1px solid #aaaaaa; + background: #F2F2F2; +} +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content .layout.so-selected .layout-inside h4, +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content .layout.so-selected .layout-inside .description { + margin-left: 35px; +} +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content .layout.so-selected .layout-inside .dashicons { + display: inline-block; +} +.so-panels-dialog.so-panels-dialog-prebuilt-layouts .so-content .so-error-message { + font-size: 14px; + border: 1px solid #cccccc; + background: #F8F8F8; + padding: 15px 20px; +} +.so-panels-dialog .so-visual-styles { + margin: -15px; + /* All the field types */ +} +.so-panels-dialog .so-visual-styles h3 { + line-height: 1em; + margin: 0; + padding: 20px 15px; + border-bottom: 1px solid #ddd; +} +.so-panels-dialog .so-visual-styles .style-section-head { + background: white; + padding: 15px 10px; + border-bottom: 1px solid #ddd; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.so-panels-dialog .so-visual-styles .style-section-head h4 { + margin: 0; +} +.so-panels-dialog .so-visual-styles .style-section-fields { + padding: 15px; + border-bottom: 1px solid #ddd; + background: #F7F7F7; +} +.so-panels-dialog .so-visual-styles .style-section-fields .style-field-wrapper { + margin-bottom: 20px; +} +.so-panels-dialog .so-visual-styles .style-section-fields .style-field-wrapper:last-child { + margin-bottom: 0; +} +.so-panels-dialog .so-visual-styles .style-section-fields .style-field-wrapper > label { + font-weight: bold; + display: block; + margin-bottom: 3px; +} +.so-panels-dialog .so-visual-styles .style-section-fields .style-field-wrapper .style-field { + zoom: 1; +} +.so-panels-dialog .so-visual-styles .style-section-fields .style-field-wrapper .style-field:before { + content: ''; + display: block; +} +.so-panels-dialog .so-visual-styles .style-section-fields .style-field-wrapper .style-field:after { + content: ''; + display: table; + clear: both; +} +.so-panels-dialog .so-visual-styles .style-section-fields .style-field-wrapper .style-field input { + font-size: 12px; +} +.so-panels-dialog .so-visual-styles .style-input-wrapper { + zoom: 1; +} +.so-panels-dialog .so-visual-styles .style-input-wrapper:before { + content: ''; + display: block; +} +.so-panels-dialog .so-visual-styles .style-input-wrapper:after { + content: ''; + display: table; + clear: both; +} +.so-panels-dialog .so-visual-styles .style-input-wrapper input { + max-width: 100%; +} +.so-panels-dialog .so-visual-styles .style-field-measurement input[type="text"] { + width: 60px; + float: left; +} +.so-panels-dialog .so-visual-styles .style-field-measurement select { + float: left; +} +.so-panels-dialog .so-visual-styles .style-field-image .so-image-selector { + display: inline-block; + background-color: #f7f7f7; + border: 1px solid #ccc; + height: 28px; + float: left; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + cursor: pointer; + -webkit-box-shadow: inset 0 1px #FFFFFF; + -moz-box-shadow: inset 0 1px #FFFFFF; + box-shadow: inset 0 1px #FFFFFF; +} +.so-panels-dialog .so-visual-styles .style-field-image .so-image-selector .current-image { + height: 28px; + width: 28px; + float: left; + background: #ffffff; + border-right: 1px solid #ccc; + background-size: cover; + -webkit-border-top-right-radius: 0; + -webkit-border-bottom-right-radius: 0; + -webkit-border-bottom-left-radius: 3px; + -webkit-border-top-left-radius: 3px; + -moz-border-radius-topright: 0; + -moz-border-radius-bottomright: 0; + -moz-border-radius-bottomleft: 3px; + -moz-border-radius-topleft: 3px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; + -moz-background-clip: padding-box; + -webkit-background-clip: padding-box; + background-clip: padding-box; +} +.so-panels-dialog .so-visual-styles .style-field-image .so-image-selector .select-image { + font-size: 12px; + line-height: 28px; + float: left; + padding: 0 8px; + color: #555; +} +.so-panels-dialog .so-visual-styles .style-field-image .remove-image { + font-size: 12px; + margin-top: 4px; + margin-left: 15px; + display: inline-block; + float: left; + color: #666; + text-decoration: none; +} +.so-panels-dialog .so-visual-styles .style-field-image .remove-image .remove-image { + color: #333; +} +.so-panels-dialog .so-visual-styles .so-field-code { + font-size: 12px; + font-family: "Courier 10 Pitch", Courier, monospace; +} +.so-panels-dialog .so-visual-styles .so-description { + color: #999; + font-size: 12px; + margin-top: 5px; + margin-bottom: 0; + font-style: italic; + clear: both; +} +.so-panels-dialog .so-content .siteorigin-panels-builder .so-builder-toolbar { + border: 1px solid #dedede; +} +.so-panels-dialog .so-content .siteorigin-panels-builder .so-rows-container { + padding: 20px 0 0 0; +} +.so-panels-live-editor > div { + position: fixed; + z-index: 99999; +} +.so-panels-live-editor .live-editor-form { + display: none; +} +.so-panels-live-editor .so-overlay { + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(0, 0, 0, 0.75); +} +.so-panels-live-editor .so-sidebar { + top: 0; + left: 0; + bottom: 0; + width: 260px; + overflow-y: auto; + background: #F2F2F2; + border-right: 1px solid #D0D0D0; + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +.so-panels-live-editor .so-sidebar .so-sidebar-tools { + background: #eee; + border-bottom: 1px solid #ddd; +} +.so-panels-live-editor .so-sidebar .so-sidebar-tools .live-editor-close { + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + display: block; + width: 45px; + height: 45px; + background: #eee; + border-right: 1px solid #ddd; + color: #444; + cursor: pointer; + text-decoration: none; + position: relative; + text-align: center; + padding-top: 12px; +} +.so-panels-live-editor .so-sidebar .so-sidebar-tools .live-editor-close:hover { + background: #FFFFFF; +} +.so-panels-live-editor .so-sidebar .so-sidebar-tools .live-editor-close:before { + font: 400 22px/1 dashicons; + content: "\f341"; + top: 7px; + left: 13px; +} +.so-panels-live-editor .so-sidebar .page-widgets .page-widgets-section .section-header { + cursor: pointer; + background: white; + padding: 15px 10px; + border: solid #ddd; + border-width: 1px 0; +} +.so-panels-live-editor .so-sidebar .page-widgets .page-widgets-section .section-header h4 { + margin: 0; + font-size: 16px; +} +.so-panels-live-editor .so-sidebar .page-widgets .page-widgets-section .section-widgets { + padding: 10px; +} +.so-panels-live-editor .so-sidebar .page-widgets .page-widgets-section:first-child .section-header { + border-top: 0; +} +.so-panels-live-editor .so-sidebar .page-widgets .so-widget { + border: 1px solid #cccccc; + cursor: pointer; + padding: 10px; + background: #F8F8F8; + -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.075), inset 0 1px 0 #FFFFFF; + -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.075), inset 0 1px 0 #FFFFFF; + box-shadow: 0 1px 2px rgba(0,0,0,0.075), inset 0 1px 0 #FFFFFF; + margin-bottom: 6px; +} +.so-panels-live-editor .so-sidebar .page-widgets .so-widget:hover, +.so-panels-live-editor .so-sidebar .page-widgets .so-widget.so-hovered { + -webkit-box-shadow: 0 2px 2px rgba(0,0,0,0.0125), inset 0 1px 0 #FFFFFF; + -moz-box-shadow: 0 2px 2px rgba(0,0,0,0.0125), inset 0 1px 0 #FFFFFF; + box-shadow: 0 2px 2px rgba(0,0,0,0.0125), inset 0 1px 0 #FFFFFF; + border: 1px solid #9bafb5; + background: #f4f9fd; +} +.so-panels-live-editor .so-sidebar .page-widgets .so-widget.so-current { + border-color: #0074a2; + background: #2ea2cc; + cursor: auto; + -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.2); + -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.2); + box-shadow: 0 1px 2px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.2); +} +.so-panels-live-editor .so-sidebar .page-widgets .so-widget.so-current h4 { + color: #FFFFFF; +} +.so-panels-live-editor .so-sidebar .page-widgets .so-widget.so-current small { + color: #eeeeee; +} +.so-panels-live-editor .so-sidebar .page-widgets .so-widget.so-current:hover { + border-color: #0074a2; + background: #2ea2cc; +} +.so-panels-live-editor .so-sidebar .page-widgets .so-widget:last-child { + margin-bottom: 0; +} +.so-panels-live-editor .so-sidebar .page-widgets .so-widget h4 { + margin: 0 0 7px 0; + padding: 0; + height: 1.2em; + color: #222222; + font-size: 14px; +} +.so-panels-live-editor .so-sidebar .page-widgets .so-widget .actions { + display: none; +} +.so-panels-live-editor .so-sidebar .page-widgets .so-widget small { + font-size: 11px; + line-height: 1.2em; + height: 1.2em; + display: block; + overflow: hidden; + color: #888888; +} +.so-panels-live-editor .so-preview { + top: 0; + right: 0; + bottom: 0; + left: 260px; + background: #F4F4F4; +} +.so-panels-live-editor .so-preview iframe { + width: 100%; + height: 100%; +} +.so-panels-loading { + background-image: url("images/wpspin_light.gif"); + background-position: center center; + background-repeat: no-repeat; +} +@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + .so-panels-loading { + background-image: url(images/wpspin_light-2x.gif); + background-size: 16px 16px; + } +} +/* For the custom home page interface */ +#panels-home-page { + /* The Switch - © 2013 Thibaut Courouble - MIT License */ +} +#panels-home-page .siteorigin-panels-builder { + border: 1px solid #D0D0D0; + background-color: #ffffff; + margin: 10px 0; +} +#panels-home-page .siteorigin-panels-builder.so-panels-loading { + min-height: 150px; +} +#panels-home-page .switch { + margin: 0 10px 0 0; + float: left; + position: relative; + display: inline-block; + vertical-align: top; + width: 68px; + height: 24px; + padding: 3px; + background-color: white; + border-radius: 24px; + box-shadow: inset 0 -1px #ffffff, inset 0 1px 1px rgba(0, 0, 0, 0.05); + cursor: pointer; + background-image: -webkit-linear-gradient(top, #eeeeee, #ffffff 25px); + background-image: -moz-linear-gradient(top, #eeeeee, #ffffff 25px); + background-image: -o-linear-gradient(top, #eeeeee, #ffffff 25px); + background-image: linear-gradient(to bottom, #eeeeee, #ffffff 25px); +} +#panels-home-page .switch .switch-input { + position: absolute; + top: 0; + left: 0; + opacity: 0; +} +#panels-home-page .switch .switch-label { + position: relative; + display: block; + height: inherit; + font-size: 12px; + text-transform: uppercase; + background: #eceeef; + border-radius: inherit; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.15); + -webkit-transition: 0.15s ease-out; + -moz-transition: 0.15s ease-out; + -o-transition: 0.15s ease-out; + transition: 0.15s ease-out; + -webkit-transition-property: opacity background; + -moz-transition-property: opacity background; + -o-transition-property: opacity background; + transition-property: opacity background; +} +#panels-home-page .switch .switch-label:before, +#panels-home-page .switch .switch-label:after { + position: absolute; + top: 50%; + margin-top: -0.5em; + line-height: 1; + -webkit-transition: inherit; + -moz-transition: inherit; + -o-transition: inherit; + transition: inherit; +} +#panels-home-page .switch .switch-label:before { + content: attr(data-off); + right: 11px; + color: #aaa; + text-shadow: 0 1px rgba(255, 255, 255, 0.5); +} +#panels-home-page .switch .switch-label:after { + content: attr(data-on); + left: 13px; + color: white; + text-shadow: 0 1px rgba(0, 0, 0, 0.2); + opacity: 0; +} +#panels-home-page .switch .switch-input:checked ~ .switch-label { + background: #47a8d8; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15), inset 0 0 3px rgba(0, 0, 0, 0.2); +} +#panels-home-page .switch .switch-input:checked ~ .switch-label:before { + opacity: 0; +} +#panels-home-page .switch .switch-input:checked ~ .switch-label:after { + opacity: 1; +} +#panels-home-page .switch .switch-handle { + position: absolute; + top: 4px; + left: 4px; + width: 22px; + height: 22px; + background: white; + border-radius: 12px; + box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2); + background-image: -webkit-linear-gradient(top, #ffffff 40%, #f0f0f0); + background-image: -moz-linear-gradient(top, #ffffff 40%, #f0f0f0); + background-image: -o-linear-gradient(top, #ffffff 40%, #f0f0f0); + background-image: linear-gradient(to bottom, #ffffff 40%, #f0f0f0); + -webkit-transition: left 0.15s ease-out; + -moz-transition: left 0.15s ease-out; + -o-transition: left 0.15s ease-out; + transition: left 0.15s ease-out; +} +#panels-home-page .switch .switch-handle:before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + margin: -7px 0 0 -7px; + width: 14px; + height: 14px; + background: #f9f9f9; + border-radius: 7px; + box-shadow: inset 0 1px rgba(0, 0, 0, 0.02); + background-image: -webkit-linear-gradient(top, #eeeeee, #ffffff); + background-image: -moz-linear-gradient(top, #eeeeee, #ffffff); + background-image: -o-linear-gradient(top, #eeeeee, #ffffff); + background-image: linear-gradient(to bottom, #eeeeee, #ffffff); +} +#panels-home-page .switch .switch-input:checked ~ .switch-handle { + left: 48px; + box-shadow: -1px 1px 5px rgba(0, 0, 0, 0.2); +} +#panels-home-page .switch .switch-green > .switch-input:checked ~ .switch-label { + background: #4fb845; +} +#panels-home-page #panels-view-as-page { + display: inline-block; + margin-left: 50px; +} diff --git a/css/admin.less b/css/admin.less new file mode 100644 index 000000000..2ee93afc6 --- /dev/null +++ b/css/admin.less @@ -0,0 +1,1850 @@ +@import "mixins"; + +@font-face { + font-family: 'siteorigin-panels'; + src:url('icons/siteorigin-panels.eot?-yv2c11'); + src:url('icons/siteorigin-panels.eot?#iefix-yv2c11') format('embedded-opentype'), + url('icons/siteorigin-panels.woff?-yv2c11') format('woff'), + url('icons/siteorigin-panels.ttf?-yv2c11') format('truetype'), + url('icons/siteorigin-panels.svg?-yv2c11#siteorigin-panels') format('svg'); + font-weight: normal; + font-style: normal; +} + +#panels{ +} + +/* This is for the metabox */ + +#so-panels-panels { + &.attached-to-editor{ + + margin-top: 20px; + + h3.hndle { + display: none; + } + + .inside { + margin: 0 !important; + padding: 0 !important; + } + + .so-toolbar .so-switch-to-standard{ + display: block; + } + } +} + +/* Everything for the main builder interface */ + +.siteorigin-panels-builder { + + position: relative; + + .so-builder-container { + /* Top padding for the toolbar */ + padding-top: 38px; + position: relative; + } + + /* These are generic iconic buttons used in the page builder interface */ + .so-tool-button { + padding: 6px 7px; + font-size: 11px; + text-decoration: none; + line-height: 0.9em; + + float: left; + margin-right: 2px; + display: block; + visibility: visible; + position: relative; + + border: 1px solid #C0C0C0; + .gradient(#F0F0F0, #F0F0F0, #F9F9F9); + .box-shadow(~"0 1px 1px rgba(0,0,0,0.04)"); + outline: none; + .rounded(2px); + + span { + display: inline-block; + color: #666666; + text-shadow: 0 1px 0 #FFFFFF; + min-width: 10px; + text-align: center; + } + + &:hover { + background: #FFFFFF; + + span { + color: #444444; + } + } + } + + .so-builder-toolbar { + .box-sizing(border-box); + border-bottom: 1px solid #D0D0D0; + background: #F5F5F5; + line-height: 1em; + position: absolute; + z-index: 101; + white-space: nowrap; + overflow-x: hidden; + + box-shadow: 0 1px 1px rgba(0,0,0,0.04); + + top: 0; + left: 0; + width: 100%; + + padding: 6px 9px; + margin-top: 0 !important; + .clearfix(); + + /* The toolbar buttons */ + > .so-tool-button { + display: inline-block; + color: #666666; + padding-right: 8px; + + .so-panels-icon { + float: left; + margin: 0 5px 0 0; + } + + &:hover { + color: #444444; + } + } + + .so-switch-to-standard { + position: absolute; + top: 5px; + right: 10px; + + display: none; + text-decoration: none; + color: #666666; + padding: 5px 6px; + .rounded(2px); + border: 1px solid transparent; + margin-top: 2px; + font-size: 11px; + + &:hover { + background: #fafafa; + border: 1px solid #999999; + color: #444444; + } + } + } + + @media screen and (max-width: 600px) { + .so-builder-toolbar { + + padding: 10px; + + > .so-tool-button { + padding-right: 2px; + + .so-panels-icon { + font-size: 20px; + } + + span.so-button-text { + display: none; + } + } + + } + + } + + .so-rows-container{ + padding: 20px 15px 0 15px; + + .so-row-toolbar { + .clearfix(); + + .so-tool-button { + .box-sizing(border-box); + height: 22px; + padding: 5px; + font-size: 10px; + float: right; + + &.so-row-move { + cursor: move; + } + } + + margin-bottom: 4px; + + .so-dropdown-wrapper { + position: relative; + float: right; + + .so-dropdown-links-wrapper { + display: none; + z-index: 11; + position:absolute; + right: -10px; + padding: 6px 0 0 0; + top: 22px; + width: 125px; + + ul { + margin: 0; + border: 1px solid #C0C0C0; + background: #F9F9F9; + .rounded(2px); + padding: 4px 0; + .box-shadow(~"0 2px 2px rgba(0,0,0,0.1)"); + + li { + margin: 0; + + &:first-child { + border-top-width: 1px; + } + + a { + display: block; + padding: 2px 8px; + text-decoration: none; + color: #666; + font-size: 11px; + font-weight: bold; + + outline: 0 !important; + .box-shadow(none); + + &:hover { + background: #F0F0F0; + color: #444; + } + + .dashicons { + font-size: 16px; + margin: 0; + float: right; + line-height: 16px; + } + + /* Specific drop down hovers */ + + &.so-row-delete { + color: #a00; + + &:hover { + color: #FFF; + background: #a00; + } + } + } + } + + .so-pointer { + width: 12px; + height: 6px; + position: absolute; + z-index: 12; + background: url("./images/dropdown-pointer.png"); + background-size: 12px 6px; + top: 1px; + right: 18px; + } + } + + } + + &:hover { + .so-dropdown-links-wrapper { + display: block; + } + } + + } + + } + + .ui-sortable-placeholder { + visibility: visible !important; + background: #F7F7F7; + .box-sizing(border-box); + } + + .so-row-container { + margin-bottom: 20px; + .user-select(none); + + .so-cells { + .clearfix(); + margin: 0 -5px; + position: relative; + + .ui-resizable-handle.ui-resizable-w{ + width: 10px; + left: -11px; + cursor: col-resize; + background: rgba(0,150,211, 0); + background: rgba(0,150,211, 0.25); + + -webkit-transition: background 0.25s ease-in-out; + -moz-transition: background 0.25s ease-in-out; + -o-transition: background 0.25s ease-in-out; + transition: background 0.25s ease-in-out; + + &:hover{ + background: rgba(0,150,211, 0.1); + } + } + + .cell { + + .box-sizing(border-box); + float: left; + position: relative; + padding: 0 5px; + + &.so-first{ + margin-left: 0; + } + + &.so-last{ + margin-right: 0; + } + + .cell-wrapper{ + background: #e4eff4; + border: 1px solid #bcccd2; + padding: 10px 10px 4px 10px; /* 6px bottom to remove bottom margin from panels */ + height: 100%; + min-height: 70px; + } + + &.cell-selected .cell-wrapper{ + background: #cae7f4 url("images/cell-selected.png") repeat; + border-color: #9abcc7; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.2); + } + + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + &.cell-selected .cell-wrapper{ + background-size: 3px 3px; + } + } + + &, .cell-wrapper { + .box-sizing(border-box); + } + + .widgets-container { + + .so-widget { + cursor: move; + margin-bottom: 5px; + + background: #f9f9fb; + + border: 1px solid #9bafb5; + max-height: 49px; + .box-sizing(border-box); + .box-shadow(~"0 1px 2px rgba(0,0,0,0.075)"); + + + &:hover { + border: 1px solid #93a7ad; + background: #feffff; + .box-shadow(~"0 2px 2px rgba(0,0,0,0.075)"); + } + + .so-widget-wrapper{ + padding: 7px 9px; + overflow: hidden; + position: relative; + } + + h4{ + display: block; + cursor: pointer; + margin: 0 15px 3px 0; + font-weight: 600; + line-height: 1.25em; + color: #474747; + text-shadow: 0 1px 0 #FFF; + white-space: nowrap; + + span { + font-weight: normal; + display: inline-block; + color: #999; + text-shadow: 0 1px 0 #FFF; + margin-left: 12px; + margin-right: 5px; + font-style: italic; + } + } + + .title { + .actions { + font-size: 12px; + position: absolute; + top: 5px; + right: 7px; + cursor: pointer; + padding: 2px 2px 2px 15px; + z-index: 10; + + &:hover { + background: #feffff; + + a{ + opacity: 1; + } + } + + a{ + display: none; + margin-right: 3px; + text-decoration: none; + } + + a.widget-delete{ + color: #FF0000; + + &:hover { + color: white; + background: #FF0000; + } + } + + } + } + + &:hover { + .title a{ + display: inline-block; + opacity: 0.5; + } + } + + &.panel-being-dragged .title .actions { + display: none; + } + + small{ + display: block; + height: 16px; + overflow: hidden; + color: #777; + } + + .form{ + display: none; + } + + &.widget-being-dragged { + opacity: 0.9; + pointer-events: none; + } + + } + + .so-widget-sortable-highlight{ + border: 1px solid; + .box-sizing(border-box); + + height: 49px; + + background: #ddebef; + border-color: #bcccd2; + margin-bottom: 5px; + + position: relative; + + .box-shadow(~"inset 2px 2px 2px rgba(0,0,0,0.01)"); + } + + } + + .resize-handle{ + z-index: 100; + position: absolute; + top: 0; + width: 10px; + left: -5px; + cursor: col-resize; + background: #e5f4fa; + height: 100%; + + .transition(0.25s, background, ease-in-out); + + &:hover{ + background: #bfe4f3; + } + } + + &:first-child { + .resize-handle { + display: none; + } + } + } + } + + } + } + + /* Page Builder icons */ + + .so-panels-icon { + font-family: 'siteorigin-panels'; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + &.so-panels-icon-plus:before { + content: "\f067"; + } + &.so-panels-icon-eye:before { + content: "\f06e"; + } + &.so-panels-icon-arrows-v:before { + content: "\f07d"; + } + &.so-panels-icon-wrench:before { + content: "\f0ad"; + } + &.so-panels-icon-columns:before { + content: "\f0db"; + } + &.so-panels-icon-rotate-left:before { + content: "\f0e2"; + } + &.so-panels-icon-puzzle-piece:before { + content: "\f12e"; + } + &.so-panels-icon-cubes:before { + content: "\f1b3"; + } + } + + .so-panels-welcome-message { + text-align: center; + padding: 0px 15px 20px 15px; + color: #555; + line-height: 1.8em; + + .so-message-wrapper { + padding: 15px 10px; + background: #F8F8F8; + border: 1px solid #E0E0E0; + } + + .so-tool-button { + font-size: inherit; + display: inline-block; + float: none; + color: #666; + padding: 5px 10px; + margin: 0 3px; + + .so-panels-icon { + color: #777; + font-size: 0.8em; + } + } + } + +} + +/* This is to display a draggable widget */ +.so-widget.ui-sortable-helper.widget-being-dragged { + .siteorigin-panels-builder.so-rows-container.so-row-container.so-cells.cell.widgets-container.so-widget; +} + +/* Handles displaying a builder in the WordPress widget interface */ +.widgets-holder-wrap .widget-inside { + + .siteorigin-panels-builder { + + .so-builder-container { + padding-top: 0; + } + + .so-rows-container { + padding: 10px 0 0 0; + } + + .so-builder-toolbar { + padding-left: 15px; + padding-right: 15px; + margin: 0 -15px; + } + } + +} + +.so-panels-dialog { + + @edge_spacing: 30px; + + @title_bar_height: 50px; + @toolbar_height: 58px; + @sidebar_width: 290px; + + @pane_padding: 15px; + @border_color: #D8D8D8; + + .so-overlay, .so-content, .so-title-bar, .so-toolbar, .so-left-sidebar, .so-right-sidebar { + z-index: 100000; + position: fixed; + .box-sizing(border-box); + padding: @pane_padding; + + } + + .so-content, .so-left-sidebar, .so-right-sidebar { + overflow-y: auto; + } + + .so-overlay { + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0,0,0,0.5); + } + + .so-content { + top: @edge_spacing + @title_bar_height; + left: @edge_spacing; + right: @edge_spacing; + bottom: @edge_spacing + @toolbar_height; + background-color: #fdfdfd; + overflow-x: hidden; + + .box-shadow(~"inset 0 2px 2px rgba(0,0,0,0.03)"); + + > *:first-child { + margin-top: 0; + } + + > *:last-child { + margin-bottom: 0; + } + + .so-content-tabs > * { + display: none; + } + + } + + .so-title-bar { + left: @edge_spacing; + right: @edge_spacing; + top: @edge_spacing; + height: @title_bar_height; + background-color: #fafafa; + border-bottom: 1px solid @border_color; + + h3.so-title { + margin: 0 !important; + padding: 0 !important; + } + + h3.so-parent-link { + cursor: pointer; + position: relative; + float: left; + margin: 0 @pane_padding 0 0 !important; + padding: 0 @pane_padding+12px 0 0 !important; + + .so-separator { + position: absolute; + top: -@pane_padding; + right: 0; + width: 12px; + height: @title_bar_height; + display: block; + background: url(./images/dialog-separator.png) no-repeat; + } + + + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + .so-separator { + background: url(./images/dialog-separator@2x.png) no-repeat; + background-size: cover; + } + } + + + } + + + + /* These are the action buttons in the title bar */ + a { + position: absolute; + box-sizing: border-box; + width: 50px; + height: 50px; + display: block; + + top: 0; + right: 0; + + .transition(0.2s); + background: #fafafa; + border-left: 1px solid #d8d8d8; + border-bottom: 1px solid #d8d8d8; + + &:hover { + background: #e9e9e9; + .so-dialog-icon { + color: #333333; + } + } + + .so-dialog-icon { + position: absolute; + top: 50%; + left: 50%; + text-decoration: none; + width: 20px; + height: 20px; + margin-left: -10px; + margin-top: -10px; + color: #666666; + text-align: center; + + &:before { + font: 400 20px/1em dashicons; + + top: 7px; + left: 13px; + } + } + + &.so-close { + right: 0; + + .so-dialog-icon:before { + content: "\f335"; + } + } + + &.so-next { + right: 50px; + + .so-dialog-icon:before { + content: '\f345'; + } + } + + &.so-previous { + right: 100px; + + .so-dialog-icon:before { + content: '\f341'; + } + } + + /* Disabled nav */ + &.so-nav.so-disabled { + cursor: default; + pointer-events:none; + + .so-dialog-icon { + color: #dddddd; + } + } + + } + } + + .so-toolbar { + left: @edge_spacing; + right: @edge_spacing; + bottom: @edge_spacing; + height: @toolbar_height; + background-color: #fafafa; + border-top: 1px solid @border_color; + + .so-status { + float: left; + padding-top: 6px; + padding-bottom: 6px; + font-style: italic; + color: #999999; + line-height: 1em; + + &.so-panels-loading { + padding-left: 26px; + background-position: left center; + } + } + + .so-buttons { + + .action-buttons { + + position: absolute; + left: 15px; + top: 50%; + margin-top: -0.65em; + + a { + display: inline; + padding: 0.2em 0.5em; + line-height: 1em; + margin-right: 0.5em; + text-decoration: none; + } + + .so-delete { + color: #a00; + + &:hover { + background: #a00; + color: #FFFFFF; + } + } + + .so-duplicate:hover { + text-decoration: underline; + } + + } + + float: right; + } + } + + .so-left-sidebar, .so-right-sidebar { + background-color: #f3f3f3; + } + + .so-left-sidebar { + display: none; + + top: @edge_spacing; + left: @edge_spacing; + bottom: @edge_spacing; + width: @sidebar_width; + + + border-right: 1px solid @border_color; + + h4 { + margin: 0 0 20px 0; + font-size: 18px; + } + + .so-sidebar-search { + width: 100%; + padding: 6px; + margin-bottom: 20px; + } + + .so-sidebar-tabs { + list-style: none; + margin: 0 -15px; + + li { + margin-bottom: 0; + a { + padding: 7px 16px; + display: block; + font-size: 14px; + text-decoration: none; + + &:hover { + background: #FFFFFF; + } + + box-shadow: none !important; + } + + &.tab-active { + a { + color: #555; + font-weight: bold; + background: #FFFFFF; + &:hover { + background: #FFFFFF; + } + } + } + } + } + + } + + .so-right-sidebar { + display: none; + + top: @edge_spacing + @title_bar_height; + right: @edge_spacing; + bottom: @edge_spacing + @toolbar_height; + width: @sidebar_width; + + border-left: 1px solid @border_color; + + h3 { + color: #333; + + &:first-child { + margin-top: 0; + } + } + } + + .so-sidebar { + .form-field { + margin-bottom: 20px; + + label{ + font-weight: 500; + font-size: 15px; + display: block; + margin-bottom: 10px; + } + + input[type=text] { + + } + } + } + + &.so-panels-dialog-has-left-sidebar { + .so-content, .so-toolbar, .so-title-bar { + left: @edge_spacing + @sidebar_width; + } + + .so-content { + .box-shadow(~"inset 2px 2px 2px rgba(0,0,0,0.03)"); + } + + .so-left-sidebar { + display: block; + } + } + + &.so-panels-dialog-has-right-sidebar { + .so-content { + right: @edge_spacing + @sidebar_width; + } + + .so-right-sidebar { + display: block; + } + } + + &.so-panels-dialog-edit-widget { + + .so-left-sidebar { + + .so-widgets { + + .so-widget { + .rounded(2px); + + border: 1px solid #cccccc; + cursor: pointer; + padding: 10px; + background: #f9f9f9; + .box-shadow(~"0 1px 2px rgba(0,0,0,0.075), inset 0 1px 0 #FFFFFF"); + margin-bottom: 15px; + + &:hover { + border: 1px solid #BBBBBB; + background: #FFFFFF; + } + + &.so-current { + border-color: #0074a2; + background: #2ea2cc; + cursor: auto; + .box-shadow(~"0 1px 2px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.2)"); + + h3 { + color: #FFFFFF; + } + + small { + color: #eeeeee; + } + + &:hover { + border-color: #0074a2; + background: #2ea2cc; + } + } + + &:last-child { + margin-bottom: 0; + } + + h3 { + margin: 0 0 7px 0; + padding: 0; + height: 1.2em; + color: #222222; + font-size: 14px; + } + + small{ + font-size: 11px; + line-height: 1.25em; + display: block; + overflow: hidden; + color: #888888; + } + + } + + } + + } + + } + + /* The add widget dialog */ + + &.so-panels-dialog-add-widget { + + .widget-type-list { + + .clearfix(); + + margin: 0 -5px -10px -5px; + min-height: 10px; + + .widget-type { + -ms-user-select: none; /* IE 10+ */ + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; + user-select: none; + + .box-sizing(border-box); + + width: 25%; + padding: 0 5px; + margin-bottom: 10px; + float: left; + + @media (max-width: 1280px) { + & { + width: 33.333%; + } + } + + @media (max-width: 960px) { + & { + width: 50%; + } + } + + h3 { + margin: 0 0 7px 0; + padding: 0; + height: 1.2em; + color: #222222; + font-size: 14px; + } + + small{ + font-size: 11px; + height: 2.5em; + line-height: 1.25em; + display: block; + overflow: hidden; + color: #888888; + } + + .widget-icon { + font-size: 20px; + width: 20px; + height: 20px; + color: #666; + float: left; + margin: -1px 0.5em 0 0; + } + } + + .widget-type-wrapper { + border: 1px solid #cccccc; + cursor: pointer; + padding: 10px; + background: #F8F8F8; + .box-shadow(~"0 1px 2px rgba(0,0,0,0.075)"); + + &:hover { + border: 1px solid #BBBBBB; + background: #FFFFFF; + .box-shadow(~"0 2px 2px rgba(0,0,0,0.075)"); + } + } + + } + + } + + /* The row edit dialog */ + + &.so-panels-dialog-row-edit { + + .so-content { + + @preview_height: 360px; + + .row-set-form { + .clearfix(); + padding: 8px; + border: 1px solid #ccc; + margin-bottom: 20px; + background: #F3F3F3; + + input, select, button, strong, span { + display: block; + float: left; + margin-right: 5px; + + outline: none; + box-shadow: none; + } + + strong, span { + margin-top: 5px; + margin-right: 0.75em; + } + + span { + margin-left: 0.75em; + } + + label{ + display: inline; + } + } + + .row-preview { + + margin: 0 -6px; + height: @preview_height; + position: relative; + .preview-cell, .preview-cell-in, .preview-cell-weight { + .box-sizing(border-box); + } + + .preview-cell { + float: left; + position: relative; + padding: 0 6px; + + .preview-cell-in { + border: 1px solid #bcccd2; + min-height: @preview_height; + background: #e4eff4; + position: relative; + + .preview-cell-weight, + .preview-cell-weight-input { + position: absolute; + font-size: 17px; + font-weight: bold; + top: 50%; + left: 50%; + width: 80px; + text-align: center; + color: #5e6d72; + margin: -0.95em 0 0 -40px; + padding: 10px 0; + border: 1px solid transparent; + line-height: 1.4em !important; + + &:after{ + content: '%'; + } + overflow: hidden; + cursor: pointer; + + &:hover { + background: #F6F6F6; + border: 1px solid #D0D0D0; + } + } + + .preview-cell-weight-input { + background: #F6F6F6; + border: 1px solid #D0D0D0; + .box-shadow(none); + } + } + + .resize-handle{ + z-index: 100; + position: absolute; + top: 0; + width: 12px; + left: -6px; + cursor: col-resize; + background: #e5f4fb; + height: @preview_height; + + .transition(0.15s, background, ease-in-out); + + &:hover, &.ui-draggable-dragging { + background: #b7e0f1; + } + } + } + } + + } + + + } + + &.so-panels-dialog-history { + + .so-left-sidebar { + padding: 0; + } + + .history-entries { + + + .history-entry { + padding: 10px; + background: #F8F8F8; + cursor: pointer; + + h3 { + margin: 0 0 0.6em 0; + font-size: 12px; + font-weight: bold; + color: #444444; + line-height: 1em; + } + + .timesince { + color: #999999; + font-size: 11px; + line-height: 1em; + } + + border-bottom: 1px solid #ccc; + + &:hover { + background: #F0F0F0; + } + + &.so-selected { + background: #EEEEEE; + } + + .count { + color: #999999; + } + } + } + + .so-content { + padding: 0; + overflow-y: hidden; + + form.history-form { + display: none; + } + + iframe.siteorigin-panels-history-iframe { + width: 100%; + height: 100%; + } + } + } + + /* For prebuilt layouts */ + + &.so-panels-dialog-prebuilt-layouts { + .so-content { + + padding-left: 10px; + padding-right: 10px; + + .layout { + .box-sizing(border-box); + float: left; + width: 33.333%; + padding: 0 5px 10px 5px; + + .layout-inside { + padding: 20px; + cursor: pointer; + border: 1px solid #cccccc; + background: #F8F8F8; + .box-shadow(~"0 1px 2px rgba(0,0,0,0.075)"); + + &:hover { + border: 1px solid #BBBBBB; + background: #FFFFFF; + .box-shadow(~"0 2px 2px rgba(0,0,0,0.075)"); + } + + h4 { + font-size: 15px; + margin: 0; + line-height: 1.2em; + height: 1.2em; + overflow: hidden; + } + + .description { + line-height: 1.2em; + height: 1.2em; + margin-top: 0.7em; + font-size: 12px; + color: #888; + overflow: hidden; + } + + .dashicons { + display: none; + float: left; + margin-top: 10px; + } + + } + + &.so-selected { + + .layout-inside { + border: 1px solid #aaaaaa; + background: #F2F2F2; + + h4, .description { + margin-left: 35px; + } + + .dashicons { + display: inline-block; + } + } + + } + + } + + .so-error-message { + font-size: 14px; + border: 1px solid #cccccc; + background: #F8F8F8; + padding: 15px 20px; + } + } + + } + + /* Everything we need for the style fields */ + + .so-visual-styles { + + margin: -15px; + + h3 { + line-height: 1em; + margin: 0; + padding: 20px 15px; + border-bottom: 1px solid #ddd; + } + + .style-section-head { + background: white; + padding: 15px 10px; + border-bottom: 1px solid #ddd; + cursor: pointer; + + .user-select(none); + + h4 { + margin: 0; + } + } + + .style-section-fields { + padding: 15px; + border-bottom: 1px solid #ddd; + background: #F7F7F7; + + .style-field-wrapper { + margin-bottom: 20px; + + &:last-child { + margin-bottom: 0; + } + + > label { + font-weight: bold; + display: block; + margin-bottom: 3px; + } + + .style-field { + .clearfix(); + + input { + font-size: 12px; + } + } + } + } + + .style-input-wrapper { + .clearfix(); + + input { + max-width: 100%; + } + } + + /* All the field types */ + + .style-field-measurement { + input[type="text"] { + width: 60px; + float: left; + } + + select { + float: left; + } + } + + .style-field-image { + + @image_field_height: 28px; + + .so-image-selector { + display: inline-block; + background-color: #f7f7f7; + border: 1px solid #ccc; + height: @image_field_height; + float: left; + .rounded(3px); + cursor: pointer; + + .box-shadow(~"inset 0 1px #FFFFFF"); + + .current-image { + height: @image_field_height; + width: @image_field_height; + float: left; + background: #ffffff; + border-right: 1px solid #ccc; + background-size: cover; + + .border-radius(0, 0, 3px, 3px); + } + + .select-image { + font-size: 12px; + line-height: @image_field_height; + float: left; + padding: 0 8px; + color: #555; + } + } + + .remove-image { + font-size: 12px; + margin-top: 4px; + margin-left: 15px; + + display: inline-block; + float: left; + color: #666; + text-decoration: none; + + .remove-image { + color: #333; + } + } + } + + .so-field-code { + font-size: 12px; + font-family: "Courier 10 Pitch", Courier, monospace; + } + + .so-description { + color: #999; + font-size: 12px; + margin-top: 5px; + margin-bottom: 0; + font-style: italic; + clear:both; + } + + } + + /* Special case of the builder interface being inside a dialog */ + + .so-content { + .siteorigin-panels-builder { + .so-builder-toolbar { + border: 1px solid #dedede; + } + + .so-rows-container { + padding: 20px 0 0 0; + } + } + } +} + +.so-panels-live-editor { + > div { + position: fixed; + z-index: 99999; + } + + .live-editor-form { + display: none; + } + + .so-overlay { + top: 0; + right: 0; + bottom: 0; + left: 0; + background: rgba(0,0,0,0.75); + } + + .so-sidebar { + top: 0; + left: 0; + bottom: 0; + width: 260px; + overflow-y: auto; + + background: #F2F2F2; + + border-right: 1px solid #D0D0D0; + .box-sizing(border-box); + + .so-sidebar-tools { + background: #eee; + border-bottom: 1px solid #ddd; + + .live-editor-close { + .box-sizing(border-box); + display: block; + width: 45px; + height: 45px; + background: #eee; + border-right: 1px solid #ddd; + color: #444; + cursor: pointer; + text-decoration: none; + position: relative; + text-align: center; + padding-top: 12px; + + &:hover { + background: #FFFFFF; + } + + &:before { + font: 400 22px/1 dashicons; + content: "\f341"; + top: 7px; + left: 13px; + } + } + + } + + .page-widgets { + + .page-widgets-section { + + .section-header { + cursor: pointer; + background: white; + padding: 15px 10px; + border: solid #ddd; + border-width: 1px 0; + + h4 { + margin: 0; + font-size: 16px; + } + } + + .section-widgets { + padding: 10px; + } + + + &:first-child { + .section-header { + border-top: 0; + } + } + } + + .so-widget { + border: 1px solid #cccccc; + cursor: pointer; + padding: 10px; + background: #F8F8F8; + .box-shadow(~"0 1px 2px rgba(0,0,0,0.075), inset 0 1px 0 #FFFFFF"); + margin-bottom: 6px; + + &:hover, &.so-hovered { + .box-shadow(~"0 2px 2px rgba(0,0,0,0.0125), inset 0 1px 0 #FFFFFF"); + border: 1px solid #9bafb5; + background: #f4f9fd; + } + + &.so-current { + border-color: #0074a2; + background: #2ea2cc; + cursor: auto; + .box-shadow(~"0 1px 2px rgba(0,0,0,0.15), inset 0 1px 0 rgba(255,255,255,0.2)"); + + h4 { + color: #FFFFFF; + } + + small { + color: #eeeeee; + } + + &:hover { + border-color: #0074a2; + background: #2ea2cc; + } + } + + &:last-child { + margin-bottom: 0; + } + + h4 { + margin: 0 0 7px 0; + padding: 0; + height: 1.2em; + color: #222222; + font-size: 14px; + } + + .actions { + display: none; + } + + small{ + font-size: 11px; + line-height: 1.2em; + height: 1.2em; + display: block; + overflow: hidden; + color: #888888; + } + + } + + } + } + + .so-preview { + top: 0; + right: 0; + bottom: 0; + left: 260px; + + background: #F4F4F4; + + iframe{ + width: 100%; + height: 100%; + } + } + +} + +.so-panels-loading { + background-image: url("images/wpspin_light.gif"); + background-position: center center; + background-repeat: no-repeat; + + @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { + & { + background-image: url(images/wpspin_light-2x.gif); + background-size: 16px 16px; + } + } +} + + +/* For the custom home page interface */ + +#panels-home-page { + + .siteorigin-panels-builder { + border: 1px solid #D0D0D0; + background-color: #ffffff; + margin: 10px 0; + + &.so-panels-loading { + min-height: 150px; + } + } + + /* The Switch - © 2013 Thibaut Courouble - MIT License */ + + .switch { + @switch_height: 24px; + @switch_width: 68px; + @switch_padding: 3px; + + margin: 0 10px 0 0; + float: left; + position: relative; + display: inline-block; + vertical-align: top; + width: @switch_width; + height: @switch_height; + padding: @switch_padding; + background-color: white; + border-radius: @switch_height; + box-shadow: inset 0 -1px white, inset 0 1px 1px rgba(0, 0, 0, 0.05); + cursor: pointer; + background-image: -webkit-linear-gradient(top, #eeeeee, white 25px); + background-image: -moz-linear-gradient(top, #eeeeee, white 25px); + background-image: -o-linear-gradient(top, #eeeeee, white 25px); + background-image: linear-gradient(to bottom, #eeeeee, white 25px); + + .switch-input { + position: absolute; + top: 0; + left: 0; + opacity: 0; + } + + .switch-label { + position: relative; + display: block; + height: inherit; + font-size: @switch_height/2; + text-transform: uppercase; + background: #eceeef; + border-radius: inherit; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.15); + -webkit-transition: 0.15s ease-out; + -moz-transition: 0.15s ease-out; + -o-transition: 0.15s ease-out; + transition: 0.15s ease-out; + -webkit-transition-property: opacity background; + -moz-transition-property: opacity background; + -o-transition-property: opacity background; + transition-property: opacity background; + } + .switch-label:before, + .switch-label:after { + position: absolute; + top: 50%; + margin-top: -.5em; + line-height: 1; + -webkit-transition: inherit; + -moz-transition: inherit; + -o-transition: inherit; + transition: inherit; + } + .switch-label:before { + content: attr(data-off); + right: 11px; + color: #aaa; + text-shadow: 0 1px rgba(255, 255, 255, 0.5); + } + .switch-label:after { + content: attr(data-on); + left: @switch_height/2 + 1px; + color: white; + text-shadow: 0 1px rgba(0, 0, 0, 0.2); + opacity: 0; + } + .switch-input:checked ~ .switch-label { + background: #47a8d8; + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15), inset 0 0 3px rgba(0, 0, 0, 0.2); + } + .switch-input:checked ~ .switch-label:before { + opacity: 0; + } + .switch-input:checked ~ .switch-label:after { + opacity: 1; + } + + .switch-handle { + position: absolute; + top: 4px; + left: @switch_padding + 1px; + width: @switch_height - 2px; + height: @switch_height - 2px; + background: white; + border-radius: @switch_height/2; + box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2); + background-image: -webkit-linear-gradient(top, white 40%, #f0f0f0); + background-image: -moz-linear-gradient(top, white 40%, #f0f0f0); + background-image: -o-linear-gradient(top, white 40%, #f0f0f0); + background-image: linear-gradient(to bottom, white 40%, #f0f0f0); + -webkit-transition: left 0.15s ease-out; + -moz-transition: left 0.15s ease-out; + -o-transition: left 0.15s ease-out; + transition: left 0.15s ease-out; + } + .switch-handle:before { + content: ''; + position: absolute; + top: 50%; + left: 50%; + margin: -7px 0 0 -7px; + width: @switch_height/2 + 2px; + height: @switch_height/2 + 2px; + background: #f9f9f9; + border-radius: (@switch_height/2 + 2px) / 2; + box-shadow: inset 0 1px rgba(0, 0, 0, 0.02); + background-image: -webkit-linear-gradient(top, #eeeeee, white); + background-image: -moz-linear-gradient(top, #eeeeee, white); + background-image: -o-linear-gradient(top, #eeeeee, white); + background-image: linear-gradient(to bottom, #eeeeee, white); + } + .switch-input:checked ~ .switch-handle { + left: @switch_width - @switch_height + 2px + 2px; + box-shadow: -1px 1px 5px rgba(0, 0, 0, 0.2); + } + + .switch-green > .switch-input:checked ~ .switch-label { + background: #4fb845; + } + } + + #panels-view-as-page { + display: inline-block; + margin-left: 50px; + } +} \ No newline at end of file diff --git a/css/front.css b/css/front.css new file mode 100644 index 000000000..a575e8969 --- /dev/null +++ b/css/front.css @@ -0,0 +1,49 @@ +.panel-grid { + zoom: 1; +} +.panel-grid:before { + content: ''; + display: block; +} +.panel-grid:after { + content: ''; + display: table; + clear: both; +} +.panel-grid-cell { + -ms-box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + min-height: 1em; +} +.panel-grid-cell .panel { + zoom: 1; +} +.panel-grid-cell .panel:before { + content: ''; + display: block; +} +.panel-grid-cell .panel:after { + content: ''; + display: table; + clear: both; +} +.panel-grid-cell .panel.panel-last-child { + margin-bottom: 0; +} +.panel-grid-cell .widget-title { + margin-top: 0; +} +.panel-row-style { + zoom: 1; +} +.panel-row-style:before { + content: ''; + display: block; +} +.panel-row-style:after { + content: ''; + display: table; + clear: both; +} diff --git a/css/front.less b/css/front.less new file mode 100644 index 000000000..4dd71c7e9 --- /dev/null +++ b/css/front.less @@ -0,0 +1,26 @@ +@import "mixins"; + +.panel-grid { + zoom: 1; + .clearfix(); +} +.panel-grid-cell { + .box-sizing(border-box); + min-height: 1em; + + .panel { + .clearfix(); + } + + .panel.panel-last-child { + margin-bottom: 0; + } + + .widget-title { + margin-top: 0; + } +} + +.panel-row-style { + .clearfix(); +} \ No newline at end of file diff --git a/css/icons/readme.txt b/css/icons/readme.txt new file mode 100644 index 000000000..6b7f4b460 --- /dev/null +++ b/css/icons/readme.txt @@ -0,0 +1,5 @@ +Icons are a subset of FontAwesome +Font Awesome by Dave Gandy - http://fontawesome.io + +License: SIL OFL 1.1 +URL: http://scripts.sil.org/OFL \ No newline at end of file diff --git a/css/icons/siteorigin-panels.eot b/css/icons/siteorigin-panels.eot new file mode 100755 index 0000000000000000000000000000000000000000..abf66f8d12e51c56f60dd5c53b070908ddd5866c GIT binary patch literal 2868 zcmb7GU2Gdw7Cv{zGoBxN#y|7p9}*{a#_l%cr*UGZxS>mbQj9~Luv@yJg~bh_O_exR z(@GQsiYT-oEuu;#ejY%)@bdt?6fh60R0*Wzfd?LVOZR10s1Lh}+?9|(YGTfvJ4u>s zCBz-gopa7T_ndRTd+wbxr~+J300?lfwGp^NsAOz>tDNex7J2cr=JqiF4Q5~p=3x%s zd8ng;29LlroP_f*3pF^45(^$h$yr!HU8>UoyPy~PUES@oQAi!Cun0W#SO3&EpePQ9^_v-Sz9=R-w^We;a562d*SH^k6rjX@*e?+U!Iz-J>~o9 z^AB<8NgOYnLV@&-?|;a@g?!hkxpU8?vKS6X)PDc${8Y{VgRqADf00kj)t)&62D}a+ zOUQTBYje{_;~%UbzXN?7JTt#=4&%h8&^S7P_W`*A0s!|m_Z>=JXJ7Ik$P=0B-O&h! zy|w)j;MO(%dJ7-^Iv}?JKGo}>ox5?=qk(=YQU>^d`i>zAJV+?(Xr64=o6k2tX)ZVa zb*uN*wbp8@g&Jt#t|O%NZ~YzpP5o8KVVyJB=7cyU+gBP+oyBc5t6}v2tpxyB^0vib||!hFJtYx z#aQzG+}43@EXGLDEBk<3M$C4>L+Bsf7Yo#WgDhv(gvYE7BG(jDI(bE3QWj1FZ zoncn;`3Dhur_~@!d;@mkez=f^S;Tt@Mwue+8oEiCuGBPig3Ih9*&#SHP73Hd` z>FScIDwJ1LBy~-_s??F@%BrfngDBKhb+v(7>paCY(E6JH0{2tLJU!t0Cy1WC?xeVZ zn1>W`p<|Hi#I$h-*dA=|z)-*DWuE&5j$l`j!G~P~%To_X%(@AqNPZ!l0?hFf^GzpM^WYXawo5Yq~ z1t*y-i-MmVUt1y#a!eA$wWVWR!y7K+Fsl0nky~0_jG!X7xQaKZdyx1#`YvN`Qokv( zk)P`sx{;#=y15i-mZ_*nlaW3&A^ooOt9uH1>y` z718e(ssa%U?d^6fmn#(Zc%}>^TMb(_qR-Yggt#`~ZxmDa$gLkG7BxFko69u4#3{18 zz%#ZkxFcWKd8JFHqiuw)+&jk99(^{BC@Qe5TZgcZq;u(AKx7%y{7A9KO30F`$Z|T} z-pX`URe%`QKzfDEt!eM z*vwR_w?Cbxwk0`X^%Rc?s){K=NSQ1DhPh5%mnGDslgXonUNgze+!@#*>)vjQC!9~< zd0p=`qed`jn6aKX2G$q1ElmrDg7LT!q>&Rhwfq3BhHn}7U=W631Rj7KYwAu*07X`dAa!xk4No9wMb}<(%4*AXKI*}rt1Oh?h=Rm+95De)- z0!A?Kvw>);(H3aYh}Ht05FRrtLg}cgp167Qrn9{4yM6n1*Nq!-#x1Ux(TIHdM=KJM z>!MhfBN6Mz({dzgu9)G7yeLVFbY=7_N5bYWQ&T6_iuG%cf5SCrXJ^-{PrUW`+Xps; zoBychXD>$ns_*#;R38cfzJFo(-OaCu5EcR(r4oWi|IGk`L-l;icl_=y`ejydP{BK<9gf4dL?m_cF_++O@W<&Sd;{69<5+bOwqr;Peuu~$!KPi(aa{JnN3DB zn~WB5?rv``v$?g;zoEzU)@34|~;WFGbO<-zahS_AAsTx@7 + + +Generated by IcoMoon + + + + + + + + + + + + + + \ No newline at end of file diff --git a/css/icons/siteorigin-panels.ttf b/css/icons/siteorigin-panels.ttf new file mode 100755 index 0000000000000000000000000000000000000000..8838c0c383f59e006016fe7a2aa2ec7a0029b8f8 GIT binary patch literal 2704 zcmahLOKclO^v$k!?cKG#{_Mve5<7O*E=}`koY*ODDD;zJ9B>1)CA5XQX=qa=PSvy$ z1%V<8ZIM7lsl?|1;=<V^h`RKj>F}1R(bz{@Ri1%yC=;h<}8u=g8#g z=O5X3`g_Db0}#JGI$k~Oy7~2|$U2Dp{81!$K5+ew_`udem^HTn$`6eMhhg zJa~|nTs*Q^TYP!(i^ciH-*0r?xYSr`G*ANLyDNt@{!l+q-&NmGpHVy2tpBS2X~(v$ z^uLJ!L3`?&*f6HTCOg#Cc<)Us@fKf$4FKW(zCyl8*xYLxEhJ~pnp^B3x;>||nL(1q zxgP=nb0H8gYGxp?fFDE6n!!-=@zkA+|Dc!+tzO9m+&tE7BRq=!(S0FLeVPV!=N73a z^p!(Gt{0h^tj(|k=qreBOJfWgG8O3W%VyFkx2S`%>-@QQ-Z^)Em$C|#hG$NkJ$qti zc<-5XxqR$cxtu<;mq;FuSMhk#>5VvhJj%|#9GTO!GZh?XG%c*XR;lblD12(i(9n)k z=e8AxCd$LZ<%yx)0;{&E4kbwxF1%dPUiSJtjGPZOB!SmZy^|Eg4j89kRZ)e z*HnUOc9TrMB`a#Fq$;v}QSqzFoTA7SmlXt6zj9HoA=CPh`H0tyS@O8{bgBHkOK0z998?9UrTO{2^dof3o zY74dng*Ms*2tYh;@sNq*$Si>skC#NjP4+L(kviGu5ya)WeO%pPmynI(Zb9VcmS#gJ z$jvU*oHNhY(02)Yllo1NmHupY=t_^~>E_Z(vra`p+Klu^8`5nW&0xVag5|--D^HD% zj1-G$9L3_u$mpTb(Fa|`RS_h~wSM#9_F_K2E^Fe*uFL0(1AV!5kB4L_f(KqvIEz3; zl0&6U$)uw`I`YH=Xzc#=Wzp>xDgqJnt*vGxo6YApJEk-(QwbU-R-fh8k9DoXo3y6( zk!v0$7BxFkt7+;^;}lq5;2CQY*7OTIuQX-KZ6P#s=NMCa^tWw z(&Nm`+Q4=i8#)Z0a4v!8RkckIYd)W*M>?YzSa;Mk{Qh9T7maE@8ac7AXE#8j?wZFv z=z{?mgoj}WZ@~)g2d&i>`yMV>cw?9XR;q>2YPAr0FA8{LWUWkg70UD%%tAI?=y&Vk zCXgTw1-w4(me;Eh@C8&K0nO*VrC~Lda0C3|kiP*uAv|_gh{9n-IduK{b!&d!b^G@1 z_N!N;44Ylh!y)O|H6s*~YNA+^LLuYYF)0+*7xZ9An)P^QX=ZelLP7nXv9UwTh1#V9 zKX8kalatGpXWu*U{`M6jo^9uiTKwwO&~xhMo1nOm2=L1*10SxgPKEG~0MHkg4%DM> zGk{=G)HUC+QwQ#_7teAXCOWrWN+XPedd&@;MjduJZe- z5`JEI3vj=1G^4N)XY#|BP7DP6NMo6C2=8=Y9^rNeb|Jjmfdw!SWAY+;=yhNjdt!{f z{w@hA)Fy+OO$IZY3}!YN%xp4PMBPn}F0(m-cBf$qziFt!9vFucFar}dmc)0s3wjV+ zBQr5JJvBXDyORh{pyClY1(Q%k19vE38&lIKA&C;>2vHYR#K++<(hk9CoJ+Qg$DA;_ NXd&(YZxp!u_!sUHsgnQz literal 0 HcmV?d00001 diff --git a/css/icons/siteorigin-panels.woff b/css/icons/siteorigin-panels.woff new file mode 100755 index 0000000000000000000000000000000000000000..3e9a22557ea470bcb872f1d94f2fa1a3d4c83f70 GIT binary patch literal 2780 zcmb6bTWlQF_1u}A*`1l)oqfz>AF|ip_3VZi+lklSb-FPDk~l1D*q9Q6n?UOrH*sXI zQJkm*)XHjT0ul)1NNM??e+52(p8(4TNJt>TM?d<}Kbs#Y%14pqAdsjOdpT$BtnC15 z)jK-(p4UC++;h*pcYaqN8v_Q|)K$>j@n!0NuGN1zFbKc`DEkdTcscy}^!(J5$UB4d zvjm@*AAfp!YLR$fBTc#lk<)V*e~Y|t0p#}y?mKbu!!t8ePoVfsl$Qv6-@W_#8Dt{w zUr6^51YY%hb7ubhQveFt*+Y=Y!```tY1C6jkWLZ!|0!+GPd$Zxm4}h;a>0O?8dLK# z$UBd|4-*_uzP9${!Xnb@2U~g~VxkY&%OC*=uLyrf`XxTHe?yTeNbhVcvXdhGZh)KD z#gAI}i60TgbBZIiF1Hu3Cr<|U%pHAz@j?P3$O#~?YyI?kWBvE*Z>_Jae{r+_=JnP_ ztA!e9;b$eR^`-u*{%8Gp{a1RQUI?#-AN6dzlYY`MU}#Twy>lVPRNhCy{9nAP=!gxt z6Cgf3R4!E*?+0zGgB9K0@XNi-bo*=}Kgx304?`qkuSFtO!;VDO5IAW!EKah&&EGEg z5s5KU_NqP*RxoFK;ePaw?#m_eXqilbUpPEAcKE`TgO#z_+W2^FcC2#nN|nzU%;vb2V(~uA zz0+#4Ww8mnaX;LYMtQ^s7)F_4?izYolvZjQI>TiSu>7#2YI?P*YpQxx3+vjlrl~}$ z8lrkwyQ(%27pj`3yFz5@nzqqItu{?L4YocO-^2aXu}%-U{uyS^ZaXP&Wz5SIb5qAt zt~1ld9pHPgy#vERi~Y&!?H$>C#>{ls`>o4v3@XNp5ikdH+1=wL73GZup?7yKCn=H? zN*_FQ6f<1y#JMBAz1HT_rg@iCR&{02DbbXAu8 z?BwP$YqApoN#0yOAv8U46~(CTmt zDV25=Y;1*HrBY>NsJQFFn5rc3z-yY|Fi0qBth%qO%bPwq@z5{O*d4oTvfnS&B_@|T zJMBcFP%7>BOc_SL9<^-DK404~=CuhgP)^+=*FH)-YJQ})muY&1Q|5JnXROWGQ7`fm;8oX7;Q-4PvU`Ck3YPh?a-Wq_0-CBQ+3cSFOh#3MK~+s8 za?ZW?rUfQ27BZ5lU?dt2+g5TQl}aW15{4NH#SQa-qJ-qQ)6?0J&Lt9jW+v0WCz~bP z0VQSim5)i9h9$vRRjB?CbKSbG1W=PEvqwt(W}2JX8Q2l)&R$Dof=?1fUGFyIMkr*M ziM}KTHjuQfa5x$XC6h*oB4_R$?FVQzeJi*JLofoPa1V^(Em+6>pj;hr@8Pn8H-;@? zraBm9tAo+KDB+D!aPoz1EI(Yf%Y}G(*l)($NQQYF2!@RBgF%BqD58fL7@^?z24+)> zw;&vkgH?BYSiLgF5H@8`T{4bBaa%hXW{YI_7^T*h) z_5I(0=0hgHKmIWC*X_~E5dC-1i%SiYM{hHLSE(0t+dwFQbP+4_C1!rs?(fDaaw49OpJU&TVp>+vGU6$#EIy?(^ny zo3m(l0p<}FpaGA-44i{Sn02Wxe24q-uk1FR+3AJ(g@wlLOn3+j9jko%ts3z3Oa*ULPF=Got zwU))MH^UT+@Fa*#@%OhI5d^*|zR?`$#7#U6E>{WeuQ~&7HS;RISbGktnZX1?OkI$L zS5azYrnuHM*qU$4B3MJ{%oI1KhE5kWNrHhU!J-Gt5R{RG)RM02Pk{mvl%XOcrHFJx zRSXEhc8Dxn;*X7<)!OD_ktt3ojSX2|EEdv2l}Iv?QKeGh8j4b6h+?{osGAj|^deWV zaOx#NOal@DuIP@)oSGtYx;a7^?_{HNdrj=Z>Vjgkf%C0*0XWuzfwD-G2`maU^k38}WO*g4lC-aNz+&AI*^w|tPxTOo{D zW(z052kf^J5`y)SjbM)#9l{n=>X7NNvCqqf>mT+~I_O1aW@KL2e+r#%vF_o^UE8u? z*Z4SM-JY8U_FW literal 0 HcmV?d00001 diff --git a/css/images/cell-width.png b/css/images/cell-width.png new file mode 100644 index 0000000000000000000000000000000000000000..b39da1013949efb89663002a44828333eac48e41 GIT binary patch literal 975 zcmaJ=F>ljA6t-G{P(_gtQ&~9NA{JubIc?mW;zo&`&`3Cn+(=~X#lALHYM-&Mjhh7t z!NAf9@iX`VWq_pvNL@Oi{s)AB4I!0tngoUhOXs`y^nKrZ@7}xDt>)v!g}V!qBrP^J zEl2Fv#JYduy7=FA$cfmNc&)>?*bX0%m`W9obtz~BWS2UWc!$qE(UK(1%YM7VJN6Uo zvOpmjMoEK6uqCNfP9x&>DFxYK??fnNC_?{s@U9+JbnY8C`4r97CY_{ED-z|D_ zc>|PEECd3|2}pyzFu|!Ik94s(XKqynBM9#s@?}yTy9G=ZQ?RDMoC^yO6p(`QtGcc~ z0vbe6g_?@A9MW-K!w`b;CyQvYw}Tz4K8__UL+)`NVO1Rr2Ff6>uy|KR#bPnj(6pRD zLqOpHKy?V?7Fe(}QvvYwn^~PN9id@EZpqX4%B%@}H z{io3rmzbXHI2~JIOb4HaV%lRd)^Cqa`r=8L4Xe^l&%3`bF3R(GyZdpjb9AqK%lW+7 zdT{Wy^{ZT{Xs6!F`FDQj<*Tz+^ziu0^Rw~Vkcg59UmvUF{9L_6kQ%*;+ybC(1_m4Zih{)C?9>v4 zq}24xJX@vryZ0+8WTx0Eg`4^s_!c;)W@LI)6{QAO`Gq7`WhYyvDB0U7*i={n4aiL` zNmQuF&B-gas<2f8n`;GRgM{^!6u?SKvTcwn`Gu2HtFfvpyx70H< zHL)}_)KM@pFf`UTG|)G))HO7;GBLL@G*f^AC7^9ZDQQ+gE^bh}fIM5JjFOT9D}DX) z@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7^KE~&-IMVSR9nfZANAQKal z@=Hr>m4GgVcp_=yk=Z7nBxq3xGDeq!wkCrKY$Q<>xAZ{cM$q+bvEw z&4cPq!R;1joO<Fu7N`ht7EZs^GuyK3&|Kl-oyLhaz}P{;oQU;1t{dn>W#t|-Z1 zpXK=>Gt@xq{0?V>$2yuR(dUl3OCL6y-%uF+c+s}R;7;W^O?qYv<6ROyR^2w69MY7_ zD}7V?tQh}<2PZ=(8P(qC4DJMKU%P|-f>RU!>ZZWTgKynKg+zP2)=U0nGroGN@=(Sj z!5N!O{UVRM7WD;cH17#{czs8NxNKC7?K?T1PL8&aBU_g$Zh5SCSs<)nZsv6ZrMpW# z1D4$`4{~CBP-VY+F& zBfGZS*nFJviplI~TK)SPgYW|{p+C+Vx1_!v*T@X`Rk-w{?g0jq?~`0OrO%6kN;FSbKbLh* G2~7Z62I&U? literal 0 HcmV?d00001 diff --git a/css/images/dialog-separator@2x.png b/css/images/dialog-separator@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2251b9f7c069a2f7febf49cb84f83464cb956a97 GIT binary patch literal 1906 zcmaJ?Yfuws6x|R&2=Y*D6fI_11Pcw>T?AtyA;csot)U925fvMf1u~LsOcsf#6~q=4 zqJk}`1w}@D08*N2Q7A&Hv;|9LYyc@^0V~$xgNP7ue6$-C?GM|X+5H}K&$;K`?|ien zmPIdirOl)P0N@(6L=;1gGssur|NSA3bK%l@AF)&IcOU7a_ znPT(CRxAtvsJ_ZLDIt|C<;rmtOJ<8<=~WsM4FF+$y+$VAfDvFKmaJ6spy7rl2vjO~ z(5g@gBGCx36y=f(Ef$**9VgG&Am=C`emEGW=aK{}jF5qPRjOLY)$^biyj*f_+lC?V zMF_Eh2fY*}l`I2=xE2FLS)oC4Bp3zR92OcH!a-3c7>uCvU=)FaIYB7O4PkT9`QWPu zBC}}~N!%Dwl>FMdL^m!~?n+&5I4hKep;o#sPG9pN~Sxw0FL28}v z1cM0E$+b!ip~TgojZv0}rx84eRQj?6mF6|ATKB3=WW!*+Oar4V#8%QophWWjp(@qu zXdMxQz0vztVqM&34F<2dVfotP%JawXr%TjOx*QMYZP$&f7 zlgQ*swQU{n0w9raqtrS=rj}z-A|6Ciu#`##HxxmH;*e1Bd{ls-s5oSSKp+r?pkfYt z5l6UygHB*YxI9gTsfh`!;te+PKe0A1s5E3|5vEmc!W5BOTm`;mLJu`nqHww>(1PI_XCw2y82+PCE5wevAGX?HE@YbVPECV5>3 zqeOx@{co4zuYYJ3O!J=ZLUU$pbvDt5{Ep@fwLrr1yV-3cKD0|nAag`l#VGW+wgow;*PKHZTKfCTQ${Z{waQiJhY{T6uLxwVnqoJT)Dy>)mYzuRCi zxSBjI^&)8C4DUusL;ojh&d)cwo~(~L>9yBt+yHvtlp2jxWomw)xYGP6 zt7T>yP&=O6VUo#zsDKXoJY3=Q)xjmRBlt>IL9IvWx&2X(bB|fP)`xF81Z1`V-^Y$; zFjpHKsKvX~Cum?j&l#rV1*ZYbU)^^W5pD?z$`Qk)`gOap8#^f{d$r>$PsD^@m3b8H<*fu_fWKm7j5Cen565(Ld$DojAAn!tCN7p>y^LP94kJ{YL7qhaS0sUCLzArX;kjwP3)r z@*gsp$J)QUGO)VSk9M~NX7~eURe+CJYLwF_e?PLZgBDSp8{Ql(0&0Sw7?zvq> zZ-}>6u=kHucu~MD2gmJm+UbMwMUh3feT>64_SGiSkkO;0QOocU2&|=aApA-1P+{w5 zl{@Qx_IHu&d2DR%4oiYbJ7>jtne8(c7PK)$^nKf+J3{NEqv9Vc;@h@O?s2cOvz#?F zrPjq0n+DCj6h+?#O8*+u;ZMVV{d?=G5kA=0w71|Hbph95j z3(Qd^(ejPk%rUFimiBj%@#F2yY~w~lYch)beHFMnZSHW_p{MS^SQn%EGJQC((yq}v zjx2S`qes2qz33jr$TQ8UC)qze|I8t?$;Zm2UM|EJS5(!VIxxC$)`0L@-WmUh#D~wH c7Xwrv=fdG*&X(H;Y=6NhakQvr(Z@Uf0T~MTUjP6A literal 0 HcmV?d00001 diff --git a/css/images/dropdown-pointer.png b/css/images/dropdown-pointer.png new file mode 100644 index 0000000000000000000000000000000000000000..62694aa39c19be8b492d093fb38051fa191ae635 GIT binary patch literal 1077 zcmaJ=TSyd97#@_;EW;w9qKDH|km=54*S*Z-R=Yc{4mKw1f<9C@&Kz~nxj1vS9Tn+@ zM7@MrM09xxd&-2M2)ao@AQW1?MG##edy0afm#8!AuJ+J2aORx<@PFTb`_AdM)~3pe z%@qVeREo_)3hx{7T2sCf|6OJ@gSSnnu@kkMJt!;Nkcg>fHw2Xi2Lim6kw=R>)=h0z4ju-CO@r5_^D4SO{sN`Afnm;{;{B|Cx=BUuU9r!^1T zuzk2Stqk`ooJ#K90~%Z&6X+1hz|{u~hj*i7j+e)0@0un-&IR>H$;G5Pr8bZ-Z3u!? z$fq!DJqU9Y8w`cHa2@btn`R=kpYbsq9}Mt}ALKt0N3+!)J|!gcv9J{-`w+5tn$Bjk zR5n1Fb}!9x97i*L+VA&a51%t+Alda9&b9)B03F5FETowR@EGN8GlQZe&UCQ^-CCkG zoP3*b!)RBwXqIBUk_wKJ^nX`fU-EWP3NGjSPhuxMWI;Lw9W!GqcyK-2yigXOu%V1h zJ8hZ+g)X-BnaFhdOba9uxvqholB{TkcUhA|NfIv_4w4N8ib9mcJd~!XJX;?R3K2FQ zu8#y*wy{3Ou(4z~nGA6;R)~b+1*~8y866s^fK`{ViK19f5W0mU3((e%Ks9NbI>;5y zYsGWnit^>LYVllRMX@wahW5t$*SHH;SVP`%Nxaxtl0P)C=4~wQPu22z{4lPILM-jR zxj7MAdlFPNW?r=u9cO?-R#oMg|t9?i_+Q=C{6hH(y;g z6&(5UgLt%IBp}|~x8iNp!J6vJx3>>Y%*@wLp1)c}hAu`vKc7BPx}_y1-aX8%Y8Y!j z^Q&d{QrCl%fpgzlkCols{BumL-7#M>J-)B@%J_r9g{KFey?uK)l5 literal 0 HcmV?d00001 diff --git a/css/images/tooltip-pointer.png b/css/images/tooltip-pointer.png new file mode 100644 index 0000000000000000000000000000000000000000..ee3c3174646d15617c8bbec938296ed9a57bf032 GIT binary patch literal 963 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 SZEl;GO#+TWWmJj~^ZyTE{8Q5a literal 0 HcmV?d00001 diff --git a/css/images/wpspin_light.gif b/css/images/wpspin_light.gif new file mode 100644 index 0000000000000000000000000000000000000000..7f7172727e46bc58a6c8c31bc2e244b8870acf02 GIT binary patch literal 2193 zcmbW2eNfc+8OMLgX2XvQ!OaFD!S02`4cR1%1Jq8vcE-ZK$;N2mR# zpG6w}75M$HRV@m`AWBM}|& z8W8Ox{0l5R3>bi2|DYZd;8%mb(&zAE!RVGxllHup-`O%d7wk*E+kAUI#Y6=#Z&-J3~Tu^h0-#9@Y)8H&=Q2q>yWp-+XP zoX6BIn(pqsj0WV@gOZkl2jBuPFiZ}6fK6bo(R}$kGjf0J%R3bc@yxIsCp-{1c|TGp zbMp%iL@k6}TwIX;nZcHs70RHs!}#S4)xn0+Gg8jf?}0L5gd+|3Ds$N+%!xRj9*F&zro2d-xS*u7lQ6>|J=g&f<2%)X=-qgq8} zv3)~q`_12I6>aZGFD75Ln*wujQ&W9X{Mq68lsHClTN!4xYALcPhak(#T$7|#)QaPz z?b~dQ?#6kjs@z3;x3l*=uF7#7Cu=noFpRMA;*?-#i&)uko#81vC)`!8a5#$LGG#b% zSEvIB`Y~T-D9p9e;8PSAc4a3>A^{*HFX{2lf9QdLRUlF`y&_njn|8-5Fuy2eZxzJ< z6uRDZZ6BXY>zlPqB^PBDiI3OCJ`GAA?!bjN-QW4ki@wJ`qu?L^!E*=+L})&U_+V4Z zK`jCzHH+;y>!@HUzKVw!-sVsc3_({dVA?T0+!|-vhfDet15l7AuqsEwk zW7)SA9WK*4tofGZ_kOY^Inv#I;{MLr4es2MmTz=MP3HOozqg#mh0CHjJUp}G^s`&z za7}$nxoN+v2?e0sLy|`f5FmS-$z9PX>QN|y;2g5ybXZAw1U%>3i}mA!a5&2i2Zm~D zV+$6*C#h@sx~TM+8L?Baf~cLU;kgA80*)M2e+Ip_Ta6B0n7)$dU64AG);;9= zO_h_SDCVniGR+9?+gf9%TZo26Ed%SwiKtPdDOzyA9K`CR0%k7`hY5lt1MxNF$QVgC zIxN)aHSe-yp~fytwohy9$*DN-W^Z0vECh4u<#O3eXuT)>y{)mZVOA2KwrzBxJGOt` z)BH?%M8?MaQT zm&wTfOM9y6KBnwna;+H12ZQyxTn54iHm6$*uptisd%^ptFEJNA!~HV1IniY5O?kHV zcAe|S@!5&Qw>~mNMg7Rp?%|#OATdITUWDA_+ z07cO>GiGy3=Aq$HHhUA#3}48ZaV?v^;xbb!6wB|#eG?PX z5*vF{T+Jylj6Yu~1^`ptY$Yj&79E0O88Q_Pt(L4dT3cc&aZ%q636fw{Rq|4g(=Uvx z`X)T6p``&E`yOYf5MKP*d4_$&)jA3#W|4XLk+$OmmS#B7 NzBI*tq&xP_e*mwyj2-|0 literal 0 HcmV?d00001 diff --git a/css/mixins.less b/css/mixins.less new file mode 100644 index 000000000..5bd6dd754 --- /dev/null +++ b/css/mixins.less @@ -0,0 +1,173 @@ +.gradient(@color: #F5F5F5, @start: #EEE, @stop: #FFF) { + background: @color; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, @start), color-stop(1, @stop)); + background: -ms-linear-gradient(bottom,@start,@stop); + background: -moz-linear-gradient(center bottom,@start 0%,@stop 100%); + background: -o-linear-gradient(@stop,@start); + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)", @stop, @start)); +} + +.bw-gradient(@color: #F5F5F5, @start: 0, @stop: 255) { + background: @color; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, rgb(@start,@start,@start)), color-stop(1, rgb(@stop,@stop,@stop))); + background: -ms-linear-gradient(bottom, rgb(@start,@start,@start) 0%, rgb(@stop,@stop,@stop) 100%); + background: -moz-linear-gradient(center bottom, rgb(@start,@start,@start) 0%, rgb(@stop,@stop,@stop) 100%); + background: -o-linear-gradient(rgb(@stop,@stop,@stop), rgb(@start,@start,@start)); + background: linear-gradient(rgb(@stop,@stop,@stop), rgb(@start,@start,@start)); + + filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)",rgb(@stop,@stop,@stop), rgb(@start,@start,@start))); +} + +.linear-gradient(@color, @gradient) { + background: @color; + background: -moz-linear-gradient(@gradient); + background: -webkit-linear-gradient(@gradient); + background: -o-linear-gradient(@gradient); + background: -ms-linear-gradient(@gradient); + background: linear-gradient(@gradient); +} + +.bordered(@top-color: #EEE, @right-color: #EEE, @bottom-color: #EEE, @left-color: #EEE) { + border-top: solid 1px @top-color; + border-left: solid 1px @left-color; + border-right: solid 1px @right-color; + border-bottom: solid 1px @bottom-color; +} + +.drop-shadow(@x-axis: 0, @y-axis: 1px, @blur: 2px, @alpha: 0.1) { + -webkit-box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha); + -moz-box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha); + box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha); +} + +.box-shadow(@shadow) { + -webkit-box-shadow: @shadow; + -moz-box-shadow: @shadow; + box-shadow: @shadow; +} + +.rounded(@radius: 2px) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; +} + +.border-radius(@topright: 0, @bottomright: 0, @bottomleft: 0, @topleft: 0) { + -webkit-border-top-right-radius: @topright; + -webkit-border-bottom-right-radius: @bottomright; + -webkit-border-bottom-left-radius: @bottomleft; + -webkit-border-top-left-radius: @topleft; + -moz-border-radius-topright: @topright; + -moz-border-radius-bottomright: @bottomright; + -moz-border-radius-bottomleft: @bottomleft; + -moz-border-radius-topleft: @topleft; + border-top-right-radius: @topright; + border-bottom-right-radius: @bottomright; + border-bottom-left-radius: @bottomleft; + border-top-left-radius: @topleft; + .background-clip(padding-box); +} + +.opacity(@opacity: 0.5) { + -moz-opacity: @opacity; + -khtml-opacity: @opacity; + -webkit-opacity: @opacity; + opacity: @opacity; + @opperc: @opacity * 100; + -ms-filter: ~"progid:DXImageTransform.Microsoft.Alpha(opacity=@{opperc})"; + filter: ~"alpha(opacity=@{opperc})"; +} + +.transition-duration(@duration: 0.2s) { + -moz-transition-duration: @duration; + -webkit-transition-duration: @duration; + -o-transition-duration: @duration; + transition-duration: @duration; +} + +.transform(...) { + -webkit-transform: @arguments; + -moz-transform: @arguments; + -o-transform: @arguments; + -ms-transform: @arguments; + transform: @arguments; +} + +.rotation(@deg:5deg) { + .transform(rotate(@deg)); +} + +.scale(@ratio:1.5) { + .transform(scale(@ratio)); +} + +.transition(@duration:0.2s, @on: all, @ease:ease) { + -webkit-transition: @on @duration @ease; + -moz-transition: @on @duration @ease; + -o-transition: @on @duration @ease; + transition: @on @duration @ease; +} + +.inner-shadow(@horizontal:0, @vertical:1px, @blur:2px, @alpha: 0.4) { + -webkit-box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha); + -moz-box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha); + box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha); +} + +.box-sizing(@sizing: border-box) { + -ms-box-sizing: @sizing; + -moz-box-sizing: @sizing; + -webkit-box-sizing: @sizing; + box-sizing: @sizing; +} + +.user-select(@argument: none) { + -webkit-user-select: @argument; + -moz-user-select: @argument; + -ms-user-select: @argument; + user-select: @argument; +} + +.columns(@colwidth: 250px, @colcount: 0, @colgap: 50px, @columnRuleColor: #EEE, @columnRuleStyle: solid, @columnRuleWidth: 1px) { + -moz-column-width: @colwidth; + -moz-column-count: @colcount; + -moz-column-gap: @colgap; + -moz-column-rule-color: @columnRuleColor; + -moz-column-rule-style: @columnRuleStyle; + -moz-column-rule-width: @columnRuleWidth; + -webkit-column-width: @colwidth; + -webkit-column-count: @colcount; + -webkit-column-gap: @colgap; + -webkit-column-rule-color: @columnRuleColor; + -webkit-column-rule-style: @columnRuleStyle; + -webkit-column-rule-width: @columnRuleWidth; + column-width: @colwidth; + column-count: @colcount; + column-gap: @colgap; + column-rule-color: @columnRuleColor; + column-rule-style: @columnRuleStyle; + column-rule-width: @columnRuleWidth; +} + +.translate(@x:0, @y:0) { + .transform(translate(@x, @y)); +} + +.background-clip(@argument: padding-box) { + -moz-background-clip: @argument; + -webkit-background-clip: @argument; + background-clip: @argument; +} + +.clearfix() { + zoom: 1; + &:before { + content: ''; + display: block; + } + &:after { + content: ''; + display: table; + clear: both; + } +} \ No newline at end of file diff --git a/inc/admin-actions.php b/inc/admin-actions.php new file mode 100644 index 000000000..c042fb88d --- /dev/null +++ b/inc/admin-actions.php @@ -0,0 +1,183 @@ + $vals) { + $return[$id] = array( + 'name' => $vals['name'], + 'description' => isset($vals['description']) ? $vals['description'] : __('No description', 'siteorigin-panels') + ); + } + + if( !empty($return) ) { + echo json_encode( $return ); + } + else { + $message = ''; + $message .= __("Your theme doesn't have any prebuilt layouts.", 'siteorigin-panels') . ' '; + $message .= __("You can still clone existing pages though.", 'siteorigin-panels') . ' '; + echo json_encode( array( + 'error_message' => $message, + ) ); + } + + + } + elseif( strpos($_REQUEST['type'], 'clone_') === 0 ) { + // Check that the user can view the given page types + $post_type = str_replace('clone_', '', $_REQUEST['type']); + global $wpdb; + + // Select only the posts with the given post type that also have panels_data + $results = $wpdb->get_results( $wpdb->prepare(" + SELECT ID, post_title, meta.meta_value + FROM {$wpdb->posts} AS posts + JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id + WHERE + posts.post_type = %s + AND meta.meta_key = 'panels_data' + AND ( posts.post_status = 'publish' OR posts.post_status = 'draft' ) + ORDER BY post_title + LIMIT 200 + ", $post_type) ); + + foreach( $results as $result ) { + $meta_value = unserialize( $result->meta_value ); + if( empty($meta_value['widgets']) ) continue; + + // Create the return array + $return[$result->ID] = array( + 'name' => $result->post_title, + 'description' => __('Clone', 'siteorigin-panels') + ); + } + + if( !empty($return) ) { + echo json_encode( $return ); + } + else { + $type_object = get_post_type_object( $post_type ); + if( empty($type_object->labels->name) ) { + $type_name = ucfirst( $post_type ); + } + else { + $type_name = $type_object->labels->name; + } + + $message = ''; + $message .= sprintf( __("There are no %s with Page Builder content to clone.", 'siteorigin-panels') , $type_name ); + echo json_encode( array( + 'error_message' => $message, + ) ); + } + + } + else { + // Send back an error + } + + exit(); +} +add_action('wp_ajax_so_panels_prebuilt_layouts', 'siteorigin_panels_ajax_prebuilt_layouts'); + +/** + * Ajax handler to get an individual prebuilt layout + */ +function siteorigin_panels_ajax_get_prebuilt_layout(){ + if( empty( $_REQUEST['type'] ) ) exit(); + if( empty( $_REQUEST['lid'] ) ) exit(); + + header('content-type: application/json'); + + if( $_REQUEST['type'] == 'prebuilt' ) { + $layouts = apply_filters( 'siteorigin_panels_prebuilt_layouts', array() ); + if( empty( $layouts[$_REQUEST['lid']] ) ) { + // Display an error message + exit(); + } + + $layout = $layouts[$_REQUEST['lid']]; + if( isset($layout['name']) ) unset($layout['name']); + + $layout = apply_filters('siteorigin_panels_prebuilt_layout', $layout); + + echo json_encode( $layout ); + exit(); + } + elseif( current_user_can('edit_post', $_REQUEST['lid']) ) { + $panels_data = get_post_meta( $_REQUEST['lid'], 'panels_data', true ); + $panels_data = apply_filters('siteorigin_panels_data', $panels_data); + echo json_encode( $panels_data ); + exit(); + } +} +add_action('wp_ajax_so_panels_get_prebuilt_layout', 'siteorigin_panels_ajax_get_prebuilt_layout'); + +/** + * Admin ajax handler for loading a single prebuilt layout. + * + * @TODO check if this is still being used + */ +function siteorigin_panels_ajax_action_prebuilt(){ + // Get any layouts that the current user could edit. + $layouts = apply_filters( 'siteorigin_panels_prebuilt_layouts', array() ); + + if( empty($_GET['layout']) ) exit(); + if( empty($layouts[$_GET['layout']]) ) exit(); + + header('content-type: application/json'); + + $layout = !empty( $layouts[$_GET['layout']] ) ? $layouts[$_GET['layout']] : array(); + $layout = apply_filters('siteorigin_panels_prebuilt_layout', $layout); + + echo json_encode( $layout ); + exit(); +} +add_action('wp_ajax_so_panels_prebuilt_layout', 'siteorigin_panels_ajax_action_prebuilt'); \ No newline at end of file diff --git a/inc/css.php b/inc/css.php new file mode 100644 index 000000000..fda60f37d --- /dev/null +++ b/inc/css.php @@ -0,0 +1,129 @@ +css = array(); + } + + /** + * Add some general CSS. + * + * @param string $selector + * @param array $attributes + * @param int $resolution The pixel resolution that this applies to + */ + function add_css($selector, $attributes, $resolution = 1920) { + $attribute_string = ''; + foreach( $attributes as $k => $v ) { + $attribute_string[] = $k.':'.$v; + } + $attribute_string = implode(';', $attribute_string); + + // Add everything we need to the CSS selector + if( empty( $this->css[$resolution] ) ) $this->css[$resolution] = array(); + if( empty( $this->css[$resolution][$attribute_string] ) ) $this->css[$resolution][$attribute_string] = array(); + $this->css[$resolution][$attribute_string][] = $selector; + } + + /** + * Add CSS that applies to a row or group of rows. + * + * @param int $li The layout ID. If false, then the CSS applies to all layouts. + * @param int|bool $ri The row index. If false, then the CSS applies to all rows. + * @param string $sub_selector A sub selector if we need one. + * @param array $attributes An array of attributes. + * @param int $resolution The pixel resolution that this applies to + * @param bool $specify_layout Sometimes for CSS specificity, we need to include the layout ID. + */ + function add_row_css($li, $ri = false, $sub_selector = '', $attributes = array(), $resolution = 1920, $specify_layout = false) { + $selector = array(); + + if( $ri === false ) { + // This applies to all rows + $selector[] = '#pl-'.$li; + $selector[] = '.panel-grid'; + } + else { + // This applies to a specific row + if( $specify_layout ) $selector[] = '#pl-'.$li; + $selector[] = '#pg-'.$li.'-'.$ri; + } + + // Add in the sub selector + if( !empty($sub_selector) ) $selector[] = $sub_selector; + + // Add this to the CSS array + $this->add_css( implode(' ', $selector), $attributes, $resolution ); + } + + /** + * @param int $li The layout ID. If false, then the CSS applies to all layouts. + * @param int|bool $ri The row index. If false, then the CSS applies to all rows. + * @param int|bool $ci The cell index. If false, then the CSS applies to all rows. + * @param string $sub_selector A sub selector if we need one. + * @param array $attributes An array of attributes. + * @param int $resolution The pixel resolution that this applies to + * @param bool $specify_layout Sometimes for CSS specificity, we need to include the layout ID. + */ + function add_cell_css( $li, $ri = false, $ci = false, $sub_selector = '', $attributes = array(), $resolution = 1920, $specify_layout = false) { + $selector = array(); + + if( $ri === false && $ci === false ) { + // This applies to all cells in the layout + $selector[] = '#pl-'.$li; + $selector[] = '.panel-grid-cell'; + } + elseif( $ri !== false && $ci === false ) { + // This applies to all cells in a row + if( $specify_layout ) $selector[] = '#pl-'.$li; + $selector[] = '#pg-'.$li.'-'.$ri; + $selector[] = '.panel-grid-cell'; + } + elseif( $ri !== false && $ci !== false ) { + // This applies to a specific cell + if( $specify_layout ) $selector[] = '#pl-'.$li; + $selector[] = '#pgc-'.$li.'-'.$ri.'-'.$ci; + } + + // Add in the sub selector + if( !empty($sub_selector) ) $selector[] = $sub_selector; + + // Add this to the CSS array + $this->add_css( implode(' ', $selector), $attributes, $resolution ); + } + + /** + * Gets the CSS for this particular layout. + */ + function get_css(){ + // Build actual CSS from the array + $css_text = ''; + krsort( $this->css ); + foreach ( $this->css as $res => $def ) { + if ( empty( $def ) ) continue; + + if ( $res < 1920 ) { + $css_text .= '@media (max-width:' . $res . 'px)'; + $css_text .= '{ '; + } + + foreach ( $def as $property => $selector ) { + $selector = array_unique( $selector ); + $css_text .= implode( ' , ', $selector ) . ' { ' . $property . ' } '; + } + + if ( $res < 1920 ) $css_text .= ' } '; + } + + return $css_text; + } +} \ No newline at end of file diff --git a/inc/debug.php b/inc/debug.php new file mode 100644 index 000000000..1119fddeb --- /dev/null +++ b/inc/debug.php @@ -0,0 +1,19 @@ +ID, 'panels_data', true)); + } + echo "\n\n-->"; +} +add_action('siteorigin_panels_metabox_end', 'siteorigin_panels_dump'); \ No newline at end of file diff --git a/inc/default-styles.php b/inc/default-styles.php new file mode 100644 index 000000000..f23b07314 --- /dev/null +++ b/inc/default-styles.php @@ -0,0 +1,359 @@ + __('Row Class', 'siteorigin-panels'), + 'type' => 'text', + 'group' => 'attributes', + 'description' => __('A CSS class', 'siteorigin-panels'), + 'priority' => 5, + ); + + $fields['cell_class'] = array( + 'name' => __('Cell Class', 'siteorigin-panels'), + 'type' => 'text', + 'group' => 'attributes', + 'description' => __('Class added to all cells in this row.', 'siteorigin-panels'), + 'priority' => 6, + ); + + $fields['row_css'] = array( + 'name' => __('CSS Styles', 'siteorigin-panels'), + 'type' => 'code', + 'group' => 'attributes', + 'description' => __('CSS Styles, given as one per row.', 'siteorigin-panels'), + 'priority' => 10, + ); + + // Add the layout fields + + $fields['bottom_margin'] = array( + 'name' => __('Bottom Margin', 'siteorigin-panels'), + 'type' => 'measurement', + 'group' => 'layout', + 'description' => __('Space below the row.', 'siteorigin-panels'), + 'priority' => 5, + ); + + $fields['gutter'] = array( + 'name' => __('Gutter', 'siteorigin-panels'), + 'type' => 'measurement', + 'group' => 'layout', + 'description' => __('Amount of space between columns.', 'siteorigin-panels'), + 'priority' => 6, + ); + + $fields['padding'] = array( + 'name' => __('Padding', 'siteorigin-panels'), + 'type' => 'measurement', + 'group' => 'layout', + 'description' => __('Padding around the entire row.', 'siteorigin-panels'), + 'priority' => 7, + ); + + $fields['row_stretch'] = array( + 'name' => __('Row Layout', 'siteorigin-panels'), + 'type' => 'select', + 'group' => 'layout', + 'options' => array( + '' => __('Standard', 'siteorigin-panels'), + 'full' => __('Full Width', 'siteorigin-panels'), + 'full-stretched' => __('Full Width Stretched', 'siteorigin-panels'), + ), + 'priority' => 10, + ); + + // How lets add the design fields + + $fields['background'] = array( + 'name' => __('Background Color', 'siteorigin-panels'), + 'type' => 'color', + 'group' => 'design', + 'description' => __('Background color of the row.', 'siteorigin-panels'), + 'priority' => 5, + ); + + $fields['background_image_attachment'] = array( + 'name' => __('Background Image', 'siteorigin-panels'), + 'type' => 'image', + 'group' => 'design', + 'description' => __('Background image of the row.', 'siteorigin-panels'), + 'priority' => 6, + ); + + $fields['background_display'] = array( + 'name' => __('Background Image Display', 'siteorigin-panels'), + 'type' => 'select', + 'group' => 'design', + 'options' => array( + 'tile' => __('Tiled Image', 'siteorigin-panels'), + 'cover' => __('Cover', 'siteorigin-panels'), + 'center' => __('Centered, with original size', 'siteorigin-panels'), + ), + 'description' => __('How the background image is displayed.', 'siteorigin-panels'), + 'priority' => 7, + ); + + $fields['border_color'] = array( + 'name' => __('Border Color', 'siteorigin-panels'), + 'type' => 'color', + 'group' => 'design', + 'description' => __('Border color of the row.', 'siteorigin-panels'), + 'priority' => 10, + ); + + return $fields; + } + + static function widget_style_fields($fields) { + $fields['class'] = array( + 'name' => __('Widget Class', 'siteorigin-panels'), + 'type' => 'text', + 'group' => 'attributes', + 'description' => __('A CSS class', 'siteorigin-panels'), + 'priority' => 5, + ); + + $fields['widget_css'] = array( + 'name' => __('CSS Styles', 'siteorigin-panels'), + 'type' => 'code', + 'group' => 'attributes', + 'description' => __('CSS Styles, given as one per row.', 'siteorigin-panels'), + 'priority' => 10, + ); + + $fields['padding'] = array( + 'name' => __('Padding', 'siteorigin-panels'), + 'type' => 'measurement', + 'group' => 'layout', + 'description' => __('Padding around the entire widget.', 'siteorigin-panels'), + 'priority' => 7, + ); + + // How lets add the design fields + + $fields['background'] = array( + 'name' => __('Background Color', 'siteorigin-panels'), + 'type' => 'color', + 'group' => 'design', + 'description' => __('Background color of the widget.', 'siteorigin-panels'), + 'priority' => 5, + ); + + $fields['background_image_attachment'] = array( + 'name' => __('Background Image', 'siteorigin-panels'), + 'type' => 'image', + 'group' => 'design', + 'description' => __('Background image of the widget.', 'siteorigin-panels'), + 'priority' => 6, + ); + + $fields['background_display'] = array( + 'name' => __('Background Image Display', 'siteorigin-panels'), + 'type' => 'select', + 'group' => 'design', + 'options' => array( + 'tile' => __('Tiled Image', 'siteorigin-panels'), + 'cover' => __('Cover', 'siteorigin-panels'), + 'center' => __('Centered, with original size', 'siteorigin-panels'), + ), + 'description' => __('How the background image is displayed.', 'siteorigin-panels'), + 'priority' => 7, + ); + + $fields['border_color'] = array( + 'name' => __('Border Color', 'siteorigin-panels'), + 'type' => 'color', + 'group' => 'design', + 'description' => __('Border color of the widget.', 'siteorigin-panels'), + 'priority' => 10, + ); + + $fields['font_color'] = array( + 'name' => __('Font Color', 'siteorigin-panels'), + 'type' => 'color', + 'group' => 'design', + 'description' => __('Color of text inside this widget.', 'siteorigin-panels'), + 'priority' => 15, + ); + + return $fields; + } + + static function row_style_attributes( $attributes, $args ) { + if( !empty( $args['row_stretch'] ) ) { + $attributes['class'][] = 'siteorigin-panels-stretch'; + $attributes['data-stretch-type'] = $args['row_stretch']; + wp_enqueue_script('siteorigin-panels-front-styles'); + } + + if( !empty( $args['class'] ) ) { + $attributes['class'] = array_merge( $attributes['class'], explode(' ', $args['class']) ); + } + + if( !empty($args['row_css']) ){ + preg_match_all('/(.+?):(.+?);?$/', $args['row_css'], $matches); + + if(!empty($matches[0])){ + for($i = 0; $i < count($matches[0]); $i++) { + $attributes['style'] .= $matches[1][$i] . ':' . $matches[2][$i] . ';'; + } + } + } + + if( !empty( $args['padding'] ) ) { + $attributes['style'] .= 'padding: ' . esc_attr($args['padding']) . ';'; + } + + if( !empty( $args['background'] ) ) { + $attributes['style'] .= 'background-color:' . $args['background']. ';'; + } + + if( !empty( $args['background_image_attachment'] ) ) { + $url = wp_get_attachment_image_src( $args['background_image_attachment'], 'full' ); + + if( !empty($url) ) { + $attributes['style'] .= 'background-image: url(' . $url[0] . ');'; + } + + switch( $args['background_display'] ) { + case 'tile': + $attributes['style'] .= 'background-repeat: repeat;'; + break; + case 'cover': + $attributes['style'] .= 'background-size: cover;'; + break; + case 'center': + $attributes['style'] .= 'background-position: center center; background-repeat: no-repeat;'; + break; + } + } + + if( !empty( $args['border_color'] ) ) { + $attributes['style'] .= 'border: 1px solid ' . $args['border_color']. ';'; + } + + return $attributes; + } + + static function cell_style_attributes( $attributes, $row_args ) { + if( !empty( $row_args['cell_class'] ) ) { + if( empty($attributes['class']) ) $attributes['class'] = array(); + $attributes['class'] = array_merge( $attributes['class'], explode(' ', $row_args['cell_class']) ); + } + + return $attributes; + } + + static function widget_style_attributes( $attributes, $args ) { + if( !empty( $args['class'] ) ) { + if( empty($attributes['class']) ) $attributes['class'] = array(); + $attributes['class'] = array_merge( $attributes['class'], explode(' ', $args['class']) ); + } + + if( !empty($args['widget_css']) ){ + preg_match_all('/(.+?):(.+?);?$/', $args['widget_css'], $matches); + + if(!empty($matches[0])){ + for($i = 0; $i < count($matches[0]); $i++) { + $attributes['style'] .= $matches[1][$i] . ':' . $matches[2][$i] . ';'; + } + } + } + + if( !empty( $args['padding'] ) ) { + $attributes['style'] .= 'padding: ' . esc_attr($args['padding']) . ';'; + } + + if( !empty( $args['background'] ) ) { + $attributes['style'] .= 'background-color:' . $args['background']. ';'; + } + + if( !empty( $args['background_image_attachment'] ) ) { + $url = wp_get_attachment_image_src( $args['background_image_attachment'], 'full' ); + + if( !empty($url) ) { + $attributes['style'] .= 'background-image: url(' . $url[0] . ');'; + } + + switch( $args['background_display'] ) { + case 'tile': + $attributes['style'] .= 'background-repeat: repeat;'; + break; + case 'cover': + $attributes['style'] .= 'background-size: cover;'; + break; + case 'center': + $attributes['style'] .= 'background-position: center center; background-repeat: no-repeat;'; + break; + } + } + + if( !empty( $args['border_color'] ) ) { + $attributes['style'] .= 'border: 1px solid ' . $args['border_color']. ';'; + } + + if( !empty( $args['font_color'] ) ) { + $attributes['style'] .= 'color: ' . $args['font_color']. ';'; + } + + return $attributes; + } + + static function filter_css_object( $css, $panels_data, $post_id ) { + return $css; + } + + static function filter_row_bottom_margin( $margin, $grid ){ + if( !empty($grid['style']['bottom_margin']) ) { + $margin = $grid['style']['bottom_margin']; + } + return $margin; + } + + static function filter_row_gutter( $gutter, $grid ) { + if( !empty($grid['style']['gutter']) ) { + $gutter = $grid['style']['gutter']; + } + + return $gutter; + } + +} + +SiteOrigin_Panels_Default_Styling::init(); \ No newline at end of file diff --git a/inc/live-editor.php b/inc/live-editor.php new file mode 100644 index 000000000..91bbfb901 --- /dev/null +++ b/inc/live-editor.php @@ -0,0 +1,23 @@ + false, + 'home-page-default' => false, + 'home-template' => 'home-panels.php', + 'post-types' => array('page', 'post'), + + 'bundled-widgets' => get_option( 'siteorigin_panels_is_using_bundled', false ), + 'responsive' => true, + 'mobile-width' => 780, + + 'margin-bottom' => 30, + 'margin-sides' => 30, + 'affiliate-id' => apply_filters( 'siteorigin_panels_affiliate_id', false ), + 'copy-content' => true, + 'animations' => true, + 'inline-css' => true, + ) ) ); + $settings = wp_parse_args( $current_settings, $settings); + + // Filter these settings + $settings = apply_filters('siteorigin_panels_settings', $settings); + } + + if( !empty( $key ) ) return isset( $settings[$key] ) ? $settings[$key] : null; + return $settings; +} + +/** + * Add the options page + */ +function siteorigin_panels_options_admin_menu() { + add_options_page( __('SiteOrigin Page Builder', 'siteorigin-panels'), __('SiteOrigin Page Builder', 'siteorigin-panels'), 'manage_options', 'siteorigin_panels', 'siteorigin_panels_options_page' ); +} +add_action( 'admin_menu', 'siteorigin_panels_options_admin_menu' ); + +/** + * Display the admin page. + */ +function siteorigin_panels_options_page(){ + include plugin_dir_path(SITEORIGIN_PANELS_BASE_FILE) . '/tpl/options.php'; +} + +/** + * Display the field for selecting the post types + */ +function siteorigin_panels_options_field_post_types( $panels_post_types ){ + $all_post_types = array_values( array_merge( array( 'page' => 'page', 'post' => 'post' ), get_post_types( array( '_builtin' => false ) ) ) ); + + // These are post types we know we don't want to show + $all_post_types = array_diff($all_post_types, array( + // Meta Slider + 'ml-slider' + ) ); + + foreach($all_post_types as $type){ + $info = get_post_type_object($type); + if(empty($info->labels->name)) continue; + $checked = in_array( + $type, + $panels_post_types + ); + + ?> +
+

+ + + + +

+ + + $v){ + switch($f){ + case 'inline-css' : + case 'responsive' : + case 'copy-content' : + case 'animations' : + case 'bundled-widgets' : + $settings[$f] = !empty($settings[$f]); + break; + case 'margin-bottom' : + case 'margin-sides' : + case 'mobile-width' : + $settings[$f] = intval($settings[$f]); + break; + } + } + + // Checkbox settings + $settings['responsive'] = !empty($settings['responsive']); + $settings['copy-content'] = !empty($settings['copy-content']); + $settings['animations'] = !empty($settings['animations']); + $settings['inline-css'] = !empty($settings['inline-css']); + $settings['bundled-widgets'] = !empty($settings['bundled-widgets']); + + // Post type settings + $settings['post-types'] = $post_types; + + update_option('siteorigin_panels_settings', $settings); + + global $siteorigin_panels_settings; + $siteorigin_panels_settings = false; +} +add_action('load-settings_page_siteorigin_panels', 'siteorigin_panels_save_options'); \ No newline at end of file diff --git a/inc/plugin-activation.php b/inc/plugin-activation.php new file mode 100644 index 000000000..d5add76dc --- /dev/null +++ b/inc/plugin-activation.php @@ -0,0 +1,131 @@ + +
+ +
+ 'siteorigin_panels_plugin_activation', + 'plugin' => $plugin['slug'], + 'plugin_name' => $plugin['name'], + 'plugin_source' => $plugin['source'], + 'siteorigin-pa-install' => 'install-plugin', + ), + admin_url( 'themes.php' ) + ), + 'siteorigin-pa-install' + ); + $method = ''; // Leave blank so WP_Filesystem can populate it as necessary + $fields = array( sanitize_key( 'siteorigin-pa-install' ) ); // Extra fields to pass to WP_Filesystem + + if ( false === ( $creds = request_filesystem_credentials( $url, $method, false, false, $fields ) ) ) + return true; + + if ( ! WP_Filesystem( $creds ) ) { + request_filesystem_credentials( $url, $method, true, false, $fields ); // Setup WP_Filesystem + return true; + } + + require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; // Need for plugins_api + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; // Need for upgrade classes + + /** Prep variables for Plugin_Installer_Skin class */ + $title = sprintf( __('Installing %s', 'siteorigin-panels'), $plugin['name'] ); + $url = add_query_arg( array( 'action' => 'install-plugin', 'plugin' => $plugin['slug'] ), 'update.php' ); + if ( isset( $_GET['from'] ) ) + $url .= add_query_arg( 'from', urlencode( stripslashes( $_GET['from'] ) ), $url ); + + $nonce = 'install-plugin_' . $plugin['slug']; + + // Find the source of the plugin + $source = !empty( $plugin['source'] ) ? $plugin['source'] : 'http://downloads.wordpress.org/plugin/'.urlencode($plugin['slug']).'.zip'; + + /** Create a new instance of Plugin_Upgrader */ + $upgrader = new Plugin_Upgrader( $skin = new Plugin_Installer_Skin( compact( 'type', 'title', 'url', 'nonce', 'plugin', 'api' ) ) ); + + /** Perform the action and install the plugin from the $source urldecode() */ + $upgrader->install( $source ); + + /** Flush plugins cache so we can make sure that the installed plugins list is always up to date */ + wp_cache_flush(); + } +} + +function siteorigin_panels_plugin_activation_install_url($plugin, $plugin_name, $source = false){ + // This is to prevent the issue where this URL is called from outside the admin + if( !is_admin() || !function_exists('get_plugins') ) return false; + + $plugins = get_plugins(); + $plugins = array_keys($plugins); + + $installed = false; + foreach($plugins as $plugin_path){ + if(strpos($plugin_path, $plugin.'/') === 0) { + $installed = true; + break; + } + } + + if($installed && !is_plugin_active($plugin)){ + return wp_nonce_url( self_admin_url('plugins.php?action=activate&plugin='.$plugin_path), 'activate-plugin_'.$plugin_path); + } + elseif($installed && is_plugin_active($plugin)){ + return '#'; + } + else{ + return wp_nonce_url( + add_query_arg( + array( + 'page' => 'siteorigin_panels_plugin_activation', + 'plugin' => $plugin, + 'plugin_name' => $plugin_name, + 'plugin_source' => !empty($source) ? urlencode($source) : false, + 'siteorigin-pa-install' => 'install-plugin', + ), + admin_url( 'plugins.php' ) + ), + 'siteorigin-pa-install' + ); + } +} \ No newline at end of file diff --git a/inc/revisions.php b/inc/revisions.php new file mode 100644 index 000000000..83d82499c --- /dev/null +++ b/inc/revisions.php @@ -0,0 +1,72 @@ +base == 'post') return $fields; + + $fields['panels_data_field'] = __('Page Builder Content', 'siteorigin-panels'); + return $fields; + +} +add_filter( '_wp_post_revision_fields', 'siteorigin_panels_revisions_fields' ); + +/** + * Display the Page Builder content for the revision. + * + * @param $value + * @param $field + * @param $revision + * @return string + */ +function siteorigin_panels_revisions_field( $value, $field, $revision ) { + $parent_id = wp_is_post_revision( $revision->ID ); + $panels_data = get_metadata('post', $revision->ID, 'panels_data', true); + + if(empty($panels_data)) return ''; + return siteorigin_panels_render($parent_id, false, $panels_data); +} +add_filter( '_wp_post_revision_field_panels_data_field', 'siteorigin_panels_revisions_field', 10, 3 ); + diff --git a/inc/styles.php b/inc/styles.php new file mode 100644 index 000000000..ee681a4fc --- /dev/null +++ b/inc/styles.php @@ -0,0 +1,339 @@ +' . __('Row Styles', 'siteorigin-panels') . '', '', $current); + break; + + case 'widget': + siteorigin_panels_render_styles_fields('widget', '

' . __('Widget Styles', 'siteorigin-panels') . '

', '', $current); + } + + exit(); +} +add_action('wp_ajax_so_panels_style_form', 'siteorigin_panels_ajax_action_style_form'); + +/** + * Render all the style fields + * + * @param $section + * @param string $before + * @param string $after + * @param array $current + */ +function siteorigin_panels_render_styles_fields( $section, $before = '', $after = '', $current = array() ){ + $fields = apply_filters('siteorigin_panels_' . $section . '_style_fields', array() ); + if( empty($fields) ) return false; + + $groups = array( + 'attributes' => array( + 'name' => __('Attributes', 'siteorigin-panels'), + 'priority' => 5 + ), + 'layout' => array( + 'name' => __('Layout', 'siteorigin-panels'), + 'priority' => 10 + ), + 'design' => array( + 'name' => __('Design', 'siteorigin-panels'), + 'priority' => 15 + ), + ); + + // Check if we need a default group + foreach($fields as $field_id => $field) { + if( empty($field['group']) ) { + if( empty($groups['theme']) ) { + $groups['theme'] = array( + 'name' => __('Theme', 'siteorigin-panels'), + 'priority' => 10 + ); + } + $fields[$field_id]['group'] = 'theme'; + } + } + $groups = apply_filters('siteorigin_panels_' . $section . '_style_groups', $groups ); + + // Sort the style fields and groups by priority + uasort( $fields, 'siteorigin_panels_styles_sort_fields' ); + uasort( $groups, 'siteorigin_panels_styles_sort_fields' ); + + echo $before; + + $group_counts = array(); + foreach( $fields as $field_id => $field ) { + if(empty($group_counts[$field['group']])) $group_counts[$field['group']] = 0; + $group_counts[$field['group']]++; + } + + foreach( $groups as $group_id => $group ) { + + if( empty( $group_counts[$group_id] ) ) continue; + + ?> +
+
+

+
+ +
+ '; + switch($field['type']) { + case 'measurement' : + ?> + + + + + + +
+
> +
+ +
+ +
+ +
+ + + + + + '; + + if( !empty($field['description']) ) { + ?>

( isset( $b['priority'] ) ? $b['priority'] : 10 ) ) ? 1 : -1; +} + +/** + * Sanitize the style fields in panels_data + * + * @param $panels_data + * + * @return mixed + */ +function siteorigin_panels_styles_sanitize_all($panels_data){ + + if( !empty($panels_data['widgets']) ) { + // Sanitize the widgets + for ( $i = 0; $i < count( $panels_data['widgets'] ); $i ++ ) { + if ( empty( $panels_data['widgets'][ $i ]['panels_info']['style'] ) ) { + continue; + } + $panels_data['widgets'][ $i ]['panels_info']['style'] = siteorigin_panels_sanitize_style_fields( 'widget', $panels_data['widgets'][ $i ]['panels_info']['style'] ); + } + } + + if( !empty($panels_data['grids']) ) { + // The rows + for ( $i = 0; $i < count( $panels_data['grids'] ); $i ++ ) { + if ( empty( $panels_data['grids'][ $i ]['style'] ) ) { + continue; + } + $panels_data['grids'][ $i ]['style'] = siteorigin_panels_sanitize_style_fields( 'row', $panels_data['grids'][ $i ]['style'] ); + } + } + + if( !empty($panels_data['grid_cells']) ) { + // And finally, the cells + for ( $i = 0; $i < count( $panels_data['grid_cells'] ); $i ++ ) { + if ( empty( $panels_data['grid_cells'][ $i ]['style'] ) ) { + continue; + } + $panels_data['grid_cells'][ $i ]['style'] = siteorigin_panels_sanitize_style_fields( 'cell', $panels_data['grid_cells'][ $i ]['style'] ); + } + } + + return $panels_data; +} + +/** + * Sanitize style fields. + * + * @param $section + * @param $styles + * + * @return Sanitized styles + */ +function siteorigin_panels_sanitize_style_fields($section, $styles){ + static $fields_cache = array(); + + // Use the filter to get the fields for this section. + if( empty($fields_cache[$section]) ) { + $fields_cache[$section] = apply_filters('siteorigin_panels_' . $section . '_style_fields', array() ); + } + $fields = $fields_cache[$section]; + + $return = array(); + foreach($fields as $k => $field) { + + // Ignore this if we don't even have a value for the style + if( !isset($styles[$k]) || $styles[$k] == '' ) continue; + + switch($field['type']) { + case 'color' : + $color = $styles[$k]; + if ( preg_match('|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) $return[$k] = $color; + else $return[$k] = ''; + break; + case 'image' : + $return[$k] = !empty( $styles[$k] ) ? intval( $styles[$k] ) : false; + break; + case 'url' : + $return[$k] = esc_url_raw( $styles[$k] ); + break; + case 'checkbox' : + $return[$k] = !empty( $styles[$k] ); + break; + case 'measurement' : + preg_match('/([0-9\.,]+)(.*)/', $styles[$k], $match); + if( !empty($match[0]) && $match[0] != '' && !empty($match[2]) ) $return[$k] = $styles[$k]; + else $return[$k] = ''; + break; + case 'select' : + if( !empty( $styles[$k] ) && in_array( $styles[$k], array_keys( $field['options'] ) ) ) { + $return[$k] = $styles[$k]; + } + break; + default: + // Just pass the value through. + $return[$k] = $styles[$k]; + break; + + } + } + + return $return; +} + +/** + * Convert the single string attribute of the grid style into an array. + * + * @param $panels_data + * @return mixed + */ +function siteorigin_panels_style_update_data($panels_data){ + if(empty($panels_data['grids'])) return $panels_data; + + for($i = 0; $i < count($panels_data['grids']); $i++) { + + if( isset($panels_data['grids'][$i]['style']) && is_string($panels_data['grids'][$i]['style']) ){ + $panels_data['grids'][$i]['style'] = array('class' => $panels_data['grids'][$i]['style']); + } + + } + return $panels_data; +} +add_filter('siteorigin_panels_data', 'siteorigin_panels_style_update_data'); +add_filter('siteorigin_panels_prebuilt_layout', 'siteorigin_panels_style_update_data'); \ No newline at end of file diff --git a/inc/widgets-bundle.php b/inc/widgets-bundle.php new file mode 100644 index 000000000..9ebe807f5 --- /dev/null +++ b/inc/widgets-bundle.php @@ -0,0 +1,64 @@ + array( + 'class' => 'SiteOrigin_Widget_Button_Widget', + 'title' => __('SiteOrigin Button', 'siteorigin-panels'), + 'description' => __('A simple button', 'siteorigin-panels'), + 'installed' => false, + 'plugin' => array( + 'name' => __('SiteOrigin Widgets Bundle', 'siteorigin-panels'), + 'slug' => 'so-widgets-bundle' + ), + 'groups' => array('so-widgets-bundle'), + ), + + 'SiteOrigin_Widget_Image_Widget' => array( + 'class' => 'SiteOrigin_Widget_Image_Widget', + 'title' => __('SiteOrigin Image', 'siteorigin-panels'), + 'description' => __('Choose images from your media library.', 'siteorigin-panels'), + 'installed' => false, + 'plugin' => array( + 'name' => __('SiteOrigin Widgets Bundle', 'siteorigin-panels'), + 'slug' => 'so-widgets-bundle' + ), + 'groups' => array('so-widgets-bundle'), + ), + + 'SiteOrigin_Widget_Slider_Widget' => array( + 'class' => 'SiteOrigin_Widget_Slider_Widget', + 'title' => __('SiteOrigin Slider', 'siteorigin-panels'), + 'description' => __('A basic slider widget.', 'siteorigin-panels'), + 'installed' => false, + 'plugin' => array( + 'name' => __('SiteOrigin Widgets Bundle', 'siteorigin-panels'), + 'slug' => 'so-widgets-bundle' + ), + 'groups' => array('so-widgets-bundle'), + ), + + 'SiteOrigin_Widget_Features_Widget' => array( + 'class' => 'SiteOrigin_Widget_Features_Widget', + 'title' => __('SiteOrigin Features', 'siteorigin-panels'), + 'description' => __('Display site features as a collection of icons.', 'siteorigin-panels'), + 'installed' => false, + 'plugin' => array( + 'name' => __('SiteOrigin Widgets Bundle', 'siteorigin-panels'), + 'slug' => 'so-widgets-bundle' + ), + 'groups' => array('so-widgets-bundle'), + ), + + 'SiteOrigin_Widget_PostCarousel_Widget' => array( + 'class' => 'SiteOrigin_Widget_PostCarousel_Widget', + 'title' => __('SiteOrigin Post Carousel', 'siteorigin-panels'), + 'description' => __('Display your posts as a carousel.', 'siteorigin-panels'), + 'installed' => false, + 'plugin' => array( + 'name' => __('SiteOrigin Widgets Bundle', 'siteorigin-panels'), + 'slug' => 'so-widgets-bundle' + ), + 'groups' => array('so-widgets-bundle'), + ), +); \ No newline at end of file diff --git a/inc/widgets.php b/inc/widgets.php new file mode 100644 index 000000000..a60adb003 --- /dev/null +++ b/inc/widgets.php @@ -0,0 +1,152 @@ + 'WP_Widget_Black_Studio_TinyMCE', + 'title' => __('Visual Editor', 'siteorigin-panels'), + 'description' => __('Arbitrary text or HTML with visual editor', 'siteorigin-panels'), + 'installed' => false, + 'plugin' => array( + 'name' => __('Black Studio TinyMCE', 'siteorigin-panels'), + 'slug' => 'black-studio-tinymce-widget' + ), + 'groups' => array('recommended'), + 'icon' => 'dashicons dashicons-edit', + ); + } + else { + $widgets['WP_Widget_Black_Studio_TinyMCE']['groups'] = array('recommended'); + $widgets['WP_Widget_Black_Studio_TinyMCE']['icon'] = 'dashicons dashicons-edit'; + } + + // Add in all the widgets bundle widgets + $widgets = wp_parse_args( + $widgets, + include plugin_dir_path(__FILE__).'/widgets-bundle.php' + ); + + foreach($widgets as $class => $data) { + if( strpos( $class, 'SiteOrigin_Panels_Widgets_' ) === 0 || strpos( $class, 'SiteOrigin_Panels_Widget_' ) === 0 ) { + $widgets[$class]['groups'] = array('panels'); + } + } + + $widgets['SiteOrigin_Panels_Widgets_Layout']['icon'] = 'dashicons dashicons-analytics'; + + $wordpress_widgets = array( + 'WP_Widget_Pages', + 'WP_Widget_Links', + 'WP_Widget_Search', + 'WP_Widget_Archives', + 'WP_Widget_Meta', + 'WP_Widget_Calendar', + 'WP_Widget_Text', + 'WP_Widget_Categories', + 'WP_Widget_Recent_Posts', + 'WP_Widget_Recent_Comments', + 'WP_Widget_RSS', + 'WP_Widget_Tag_Cloud', + 'WP_Nav_Menu_Widget', + ); + + foreach($wordpress_widgets as $wordpress_widget) { + if( isset( $widgets[$wordpress_widget] ) ) { + $widgets[$wordpress_widget]['groups'] = array('wordpress'); + $widgets[$wordpress_widget]['icon'] = 'dashicons dashicons-wordpress'; + } + } + + return $widgets; + +} +add_filter('siteorigin_panels_widgets', 'siteorigin_panels_add_recommended_widgets'); + +/** + * Add tabs to the widget dialog + * + * @param $tabs + * + * @return array + */ +function siteorigin_panels_add_widgets_dialog_tabs($tabs){ + + $tabs[] = array( + 'title' => __('Widgets Bundle', 'siteorigin-panels'), + 'filter' => array( + 'groups' => array('so-widgets-bundle') + ) + ); + + $tabs[] = array( + 'title' => __('Page Builder Widgets', 'siteorigin-panels'), + 'filter' => array( + 'groups' => array('panels') + ) + ); + + $tabs[] = array( + 'title' => __('WordPress Widgets', 'siteorigin-panels'), + 'filter' => array( + 'groups' => array('wordpress') + ) + ); + + $tabs[] = array( + 'title' => __('Recommended Widgets', 'siteorigin-panels'), + 'filter' => array( + 'groups' => array('recommended') + ) + ); + + return $tabs; +} +add_filter('siteorigin_panels_widget_dialog_tabs', 'siteorigin_panels_add_widgets_dialog_tabs', 20); + +/** + * This will try restore bundled widgets. + * + * @param $object + * @param $widget + * + * @return \WP_Widget_Text + */ +function siteorigin_panels_restore_bundled_widget($object, $widget){ + + // We can skip this if there's already an object + if( !empty($object) ) return $object; + + if( strpos($widget, 'SiteOrigin_Panels_Widget_') === 0 || strpos($widget, 'SiteOrigin_Panels_Widgets_') === 0 ) { + + if( !class_exists('SiteOrigin_Panels_Widget') ) { + // Initialize the bundled widgets + include plugin_dir_path( SITEORIGIN_PANELS_BASE_FILE ) . '/widgets/widgets.php'; + + // Initialize all the widgets + origin_widgets_init(); + siteorigin_panels_widgets_init(); + + // Set this to change the default behaviour to using the bundled widgets, wont override user settings though + add_option('siteorigin_panels_is_using_bundled', true); + } + + if( class_exists($widget) ) { + $object = new $widget(); + } + } + elseif(!is_admin() && $widget == 'WP_Widget_Black_Studio_TinyMCE') { + // If the visual editor is missing, we can replace it with the text widget for now + $object = new WP_Widget_Text(); + } + + return $object; +} +add_filter('siteorigin_panels_widget_object', 'siteorigin_panels_restore_bundled_widget', 10, 2); \ No newline at end of file diff --git a/js/siteorigin-panels-history.js b/js/siteorigin-panels-history.js new file mode 100644 index 000000000..c13d56f44 --- /dev/null +++ b/js/siteorigin-panels-history.js @@ -0,0 +1,301 @@ +/** + * History browser for Page Builder. + * + * @copyright Greg Priday 2014 - + * @license GPL 3.0 http://www.gnu.org/licenses/gpl.html + */ + +( function( $, _, panelsOptions ){ + + var panels = window.siteoriginPanels; + + /** + * + */ + panels.model.historyEntry = Backbone.Model.extend( { + defaults: { + text : '', + data : '', + time: null, + count: 1 + } + } ); + + /** + * + */ + panels.collection.historyEntries = Backbone.Collection.extend( { + model: panels.model.historyEntry, + + /** + * The builder model + */ + builder: null, + + /** + * The maximum number of items in the history + */ + maxSize: 12, + + initialize: function(){ + this.on( 'add', this.onAddEntry, this ); + }, + + /** + * Add an entry to the collection. + * + * @param text The text that defines the action taken to get to this + * @param data + */ + addEntry: function(text, data) { + + if(typeof data == 'undefined' || data == null) { + data = this.builder.getPanelsData(); + } + + var entry = new panels.model.historyEntry( { + text: text, + data: JSON.stringify( data ), + time: parseInt( new Date().getTime() / 1000 ), + collection: this + } ); + + this.add( entry ); + }, + + /** + * Resize the collection so it's not bigger than this.maxSize + */ + onAddEntry: function(entry){ + + if(this.models.length > 1) { + var lastEntry = this.at(this.models.length - 2); + + if( + ( entry.get('text') == lastEntry.get('text') && entry.get('time') - lastEntry.get('time') < 15 ) + || ( entry.get('data') == lastEntry.get('data') ) + ) { + // If both entries have the same text and are within 20 seconds of each other, or have the same data, then remove most recent + this.remove( entry ); + lastEntry.set( 'count', lastEntry.get('count') + 1 ); + } + } + + // Make sure that there are not to many entries in this collection + while( this.models.length > this.maxSize ) { + this.shift(); + } + } + } ); + + /** + * The history manager is + */ + panels.dialog.history = panels.view.dialog.extend( { + + historyEntryTemplate: _.template( $('#siteorigin-panels-dialog-history-entry').html() ), + + entries: {}, + currentEntry: null, + revertEntry: null, + selectedEntry: null, + + dialogClass: 'so-panels-dialog-history', + + events: { + 'click .so-close': 'closeDialog', + 'click .so-restore': 'restoreSelectedEntry' + }, + + initializeDialog: function(){ + this.entries = new panels.collection.historyEntries(); + + this.on('open_dialog', this.setCurrentEntry, this); + this.on('open_dialog', this.renderHistoryEntries, this); + }, + + render: function(){ + // Render the dialog and attach it to the builder interface + this.renderDialog( this.parseDialogContent( $('#siteorigin-panels-dialog-history').html(), {} ) ); + + this.$('iframe.siteorigin-panels-history-iframe').load(function(){ + $(this).show(); + }); + }, + + /** + * Set the origianl entry. This should be set when creating the dialog. + * + * @param {panels.model.builder} builder + */ + setRevertEntry: function(builder){ + this.revertEntry = new panels.model.historyEntry( { + data: JSON.stringify( builder.getPanelsData() ), + time: parseInt( new Date().getTime() / 1000 ) + } ); + }, + + /** + * This is triggered when the dialog is opened. + */ + setCurrentEntry: function(){ + this.currentEntry = new panels.model.historyEntry( { + data: JSON.stringify( this.builder.model.getPanelsData() ), + time: parseInt( new Date().getTime() / 1000 ) + } ); + + this.selectedEntry = this.currentEntry; + this.previewEntry( this.currentEntry ); + this.$('.so-buttons .so-restore').addClass('disabled'); + }, + + /** + * Render the history entries + */ + renderHistoryEntries: function(){ + var c = this.$('.history-entries'); + + // Set up an interval that will display the time since every 10 seconds + var thisView = this; + + c.empty(); + + if( this.currentEntry.get('data') != this.revertEntry.get('data') || this.entries.models.length > 0 ) { + $(this.historyEntryTemplate({title: panelsOptions.loc.history['revert'], count: 1})) + .data('historyEntry', this.revertEntry) + .prependTo(c); + } + + // Now load all the entries in this.entries + this.entries.each(function(entry){ + + var html = thisView.historyEntryTemplate( { + title: panelsOptions.loc.history[ entry.get('text') ], + count: entry.get('count') + } ); + + $( html ) + .data('historyEntry', entry) + .prependTo(c); + }); + + + $(this.historyEntryTemplate({title: panelsOptions.loc.history['current'], count: 1})) + .data('historyEntry', this.currentEntry) + .addClass('so-selected') + .prependTo(c); + + // Handle loading and selecting + c.find('.history-entry').click(function(){ + var $$ = $(this); + c.find('.history-entry').not($$).removeClass('so-selected'); + $$.addClass('so-selected'); + + var entry = $$.data('historyEntry'); + + thisView.selectedEntry = entry; + + if( thisView.selectedEntry.cid != thisView.currentEntry.cid ) { + thisView.$('.so-buttons .so-restore').removeClass('disabled'); + } + else { + thisView.$('.so-buttons .so-restore').addClass('disabled'); + } + + thisView.previewEntry( entry ); + }); + + this.updateEntryTimes(); + }, + + /** + * Preview an entry + * + * @param entry + */ + previewEntry: function(entry){ + this.$('iframe.siteorigin-panels-history-iframe').hide(); + this.$('form.history-form input[name="siteorigin_panels_data"]').val( entry.get('data') ); + this.$('form.history-form').submit(); + }, + + /** + * Restore the current entry + */ + restoreSelectedEntry: function(){ + + if( this.$('.so-buttons .so-restore').hasClass('disabled') ) return false; + + if( this.currentEntry.get('data') == this.selectedEntry.get('data') ) { + this.closeDialog(); + return; + } + + // Add an entry for this restore event + if( this.selectedEntry.get('text') != 'restore' ) { + this.entries.addEntry( 'restore', this.builder.model.getPanelsData() ); + } + + this.builder.model.loadPanelsData( JSON.parse( this.selectedEntry.get('data') ) ); + + this.closeDialog(); + + return false; + }, + + /** + * Update the entry times for the list of entries down the side + */ + updateEntryTimes: function(){ + var thisView = this; + + this.$('.history-entries .history-entry').each(function(){ + var $$ = $(this); + + var time = $$.find('.timesince'); + var entry = $$.data('historyEntry'); + + time.html( thisView.timeSince( entry.get('time') ) ); + }); + }, + + /** + * Gets the time since as a nice string. + * + * @param date + */ + timeSince: function(time){ + var diff = parseInt( new Date().getTime() / 1000 ) - time; + + var parts = []; + var interval; + + // There are 3600 seconds in an hour + if( diff > 3600 ) { + interval = Math.floor( diff / 3600 ); + if(interval == 1) parts.push(panelsOptions.loc.time.hour.replace('%d', interval )); + else parts.push(panelsOptions.loc.time.hours.replace('%d', interval )); + diff -= interval * 3600 + } + + // There are 60 seconds in a minute + if( diff > 60 ) { + interval = Math.floor( diff / 60 ); + if(interval == 1) parts.push(panelsOptions.loc.time.minute.replace('%d', interval )); + else parts.push(panelsOptions.loc.time.minutes.replace('%d', interval )); + diff -= interval * 60 + } + + if( diff > 0 ) { + if(diff == 1) parts.push(panelsOptions.loc.time.second.replace('%d', diff )); + else parts.push(panelsOptions.loc.time.seconds.replace('%d', diff )); + } + + // Return the amount of time ago + return parts.length == 0 ? panelsOptions.loc.time.now : panelsOptions.loc.time.ago.replace('%s', parts.slice(0,2).join(', ') ); + + } + + } ); + +} )( jQuery, _, soPanelsOptions ); \ No newline at end of file diff --git a/js/siteorigin-panels-live-editor.js b/js/siteorigin-panels-live-editor.js new file mode 100644 index 000000000..d509d34f5 --- /dev/null +++ b/js/siteorigin-panels-live-editor.js @@ -0,0 +1,308 @@ +/** + * Handles the live editor interface in Page Builder + * + * @copyright Greg Priday 2014 - + * @license GPL 3.0 http://www.gnu.org/licenses/gpl.html + */ + +( function( $, _, panelsOptions ){ + + var panels = window.siteoriginPanels; + + /** + * Live editor handles + */ + panels.view.liveEditor = Backbone.View.extend( { + template: _.template( $('#siteorigin-panels-live-editor').html() ), + + sectionTemplate: _.template( $('#siteorigin-panels-live-editor-sidebar-section').html() ), + + postId: false, + bodyScrollTop : null, + displayed: false, + + events: { + 'click .live-editor-close': 'close' + }, + frameScrollTop: 0, + + initialize: function(){ + }, + + /** + * Render the live editor + */ + render: function(){ + this.setElement( this.template() ); + this.$el.html( this.template() ); + + var thisView = this; + + // Prevent clicks inside the iframe + this.$('iframe#siteorigin-panels-live-editor-iframe') + .load(function(){ + $(this).show(); + + var ifc = $(this).contents(); + + // Lets find all the first level grids. This is to account for the Page Builder layout widget. + ifc.find('.panel-grid .panel-grid-cell .panel.widget') + .filter(function(){ + // Filter to only include non nested + return $(this).parents('.widget_siteorigin-panels-builder').length == 0; + }) + .each(function(i, el){ + var $$ = $(el); + var widgetEdit = thisView.$('.page-widgets .so-widget').eq(i); + var overlay; + + $$ + .css({ + 'cursor' : 'pointer' + }) + .mouseenter(function(){ + widgetEdit.addClass('so-hovered'); + overlay = thisView.createPreviewOverlay( $(this) ) + }) + .mouseleave( function(){ + widgetEdit.removeClass('so-hovered'); + overlay.fadeOut('fast', function(){ $(this).remove() }); + } ) + .click(function(e){ + e.preventDefault(); + // When we click a widget, send that click to the form + widgetEdit.click(); + }); + }); + + // Prevent default clicks + ifc.find( "a").css({'pointer-events' : 'none'}).click(function(e){ + return false; + }); + + }); + }, + + /** + * Attach the live editor to the document + */ + attach: function(){ + this.$el.appendTo('body'); + }, + + setPostId: function(postId){ + this.postId = postId; + }, + + /** + * Display the live editor + */ + open: function(){ + if( this.$el.html() == '' ) this.render(); + if( this.$el.closest('body').length == 0 ) this.attach(); + + // Refresh the preview display + this.refreshWidgets(); + this.$el.show(); + + // Refresh the preview after we show the editor + this.refreshPreview(); + + // Disable page scrolling + this.bodyScrollTop = $('body').scrollTop(); + $('body').css( {overflow:'hidden'} ); + + this.displayed = true; + }, + + close: function(){ + this.$el.hide(); + $('body').css( {overflow:'auto'} ); + $('body').scrollTop( this.bodyScrollTop ); + + this.displayed = false; + + return false; + }, + + /** + * Refresh the preview display + */ + refreshPreview: function(){ + if( !this.$el.is(':visible') ) return; + + this.$('iframe#siteorigin-panels-live-editor-iframe').hide(); + + this.frameScrollTop = this.$('iframe#siteorigin-panels-live-editor-iframe').contents().find('body').scrollTop(); + + this.$('form.live-editor-form input[name="siteorigin_panels_data"]').val( JSON.stringify( this.builder.model.getPanelsData() ) ); + this.$('form.live-editor-form').submit(); + }, + + /** + * + * @param over + * @return {*|Object} + */ + createPreviewOverlay: function(over) { + var previewFrame = this.$('iframe#siteorigin-panels-live-editor-iframe'); + + // Remove any old overlays + var body = previewFrame.contents().find('body').css('position', 'relative'); + + previewFrame.contents().find('.panels-live-editor-overlay').remove(); + + // Create the new overlay + var overlayContainer = $('
').addClass('panels-live-editor-overlay').css( { + 'pointer-events' : 'none' + } ); + + var overlay = $('
').css({ + 'position' : 'absolute', + 'background' : '#000000', + 'z-index' : 1000, + 'opacity' : 0.25 + }); + + var spacing = 15; + + overlayContainer + .append( + overlay.clone().css({ + 'top' : -body.offset().top, + 'left' : 0, + 'right' : 0, + 'height' : over.offset().top - spacing + }) + ) + .append( + overlay.clone().css({ + 'bottom' : 0, + 'left' : 0, + 'right' : 0, + 'height' : Math.round( body.height() - over.offset().top - over.outerHeight() - spacing + body.offset().top - 0.01 ) + }) + ) + .append( + overlay.clone().css({ + 'top' : over.offset().top - spacing - body.offset().top, + 'left' : 0, + 'width' : over.offset().left - spacing, + 'height' : Math.round(over.outerHeight() + spacing*2) + }) + ) + .append( + overlay.clone().css({ + 'top' : over.offset().top - spacing - body.offset().top, + 'right' : 0, + 'left' : over.offset().left + over.outerWidth() + spacing, + 'height' : Math.round(over.outerHeight() + spacing*2) + }) + ); + + // Create a new overlay + previewFrame.contents().find('body').append(overlayContainer); + return overlayContainer; + }, + + /** + * Refresh the widgets in the left sidebar. + */ + refreshWidgets: function(){ + // Empty all the current widgets + this.$('.so-sidebar .page-widgets').empty(); + var previewFrame = this.$('iframe#siteorigin-panels-live-editor-iframe'); + + // Now lets move all the widgets to the sidebar + var thisView = this; + var widgetIndex = 0; + + this.builder.$('.so-row-container').each(function(ri, el) { + var row = $(el); + var widgets = row.find('.so-cells .cell .so-widget'); + + var sectionWrapper = $( thisView.sectionTemplate({ title: 'Row ' + (ri+1) }) ) + .appendTo( thisView.$('.so-sidebar .page-widgets') ); + + sectionWrapper.find('.section-header').click(function(){ + row.data('view').editSettingsHandler(); + }); + + var widgetsWrapper = sectionWrapper.find('.section-widgets'); + + widgets.each(function(i, el){ + var widget = $(this); + var widgetClone = widget.clone().show().css({ + opacity : 1 + }); + + // Remove all the action buttons from the clone + widgetClone.find('.actions').remove(); + widgetClone.find('.widget-icon').remove(); + + var thisWidgetIndex = (widgetIndex++); + var getHoverWidget = function(){ + // TODO this should target the #pl-x selector + return previewFrame.contents() + .find('#pl-' + thisView.postId + ' .panel-grid .panel-grid-cell .panel') + .filter(function(){ + // Filter to only include non nested + return $(this).parents('.widget_siteorigin-panels-builder').length == 0; + }) + .not('panel-hover-widget').eq(thisWidgetIndex); + } + + var overlay = null, hoverWidget = null; + + widgetClone + .click(function(e){ + e.preventDefault(); + widget.data('view').editHandler(); + return false; + }) + .mouseenter(function(){ + var hoverWidget = getHoverWidget(); + + // Center the iframe on the over item + + previewFrame.contents() + .find('html,body') + .clearQueue() + .animate( { + scrollTop: hoverWidget.offset().top - Math.max(30, ( Math.min( previewFrame.contents().height(), previewFrame.height() ) - hoverWidget.outerHeight() ) /2 ) + }, 750); + + // Create the overlay + overlay = thisView.createPreviewOverlay( hoverWidget ); + + }) + .mouseleave(function(){ + // Stop any scroll animations that are currently happening + previewFrame.contents() + .find('html,body') + .clearQueue(); + + if(overlay != null) { + overlay.fadeOut('fast', function(){ $(this).remove() }); + overlay = null; + } + if(hoverWidget != null) { + hoverWidget.remove(); + hoverWidget = null; + } + }) + .appendTo( widgetsWrapper ); + }); + }); + }, + + /** + * Return true if the live editor has a valid preview URL. + * @return {boolean} + */ + hasPreviewUrl: function(){ + return this.$('form.live-editor-form').attr('action') != ''; + } + } ); + +} )( jQuery, _, soPanelsOptions ); \ No newline at end of file diff --git a/js/siteorigin-panels-styles.js b/js/siteorigin-panels-styles.js new file mode 100644 index 000000000..1903a2b72 --- /dev/null +++ b/js/siteorigin-panels-styles.js @@ -0,0 +1,169 @@ +/** + * @copyright Greg Priday 2014 - + * @license GPL 3.0 http://www.gnu.org/licenses/gpl.html + */ + +( function( $, _, panelsOptions ){ + + var panels = window.siteoriginPanels; + + + /** + * The styles view handlers all the cool rendering stuff + */ + panels.view.styles = Backbone.View.extend( { + + stylesLoaded: false, + + initialize: function(){ + + }, + + /** + * Render the visual styles object. + * + * @param type + */ + render: function( stylesType ){ + if( typeof stylesType == 'undefined' ) return false; + + this.$el.addClass('so-visual-styles'); + + // Load the form + var thisView = this; + $.post( + ajaxurl, + { + action: 'so_panels_style_form', + type: stylesType, + style: this.model.get('style') + }, + function( response ){ + thisView.$el.html( response ); + thisView.setupFields(); + thisView.stylesLoaded = true; + thisView.trigger('styles_loaded'); + } + ); + }, + + /** + * Attach the style view to the DOM. + * + * @param wrapper + */ + attach: function( wrapper ){ + wrapper.append( this.$el ); + }, + + /** + * Detach the styles view from the DOM + */ + detach: function(){ + this.$el.detach(); + }, + + /** + * Setup all the fields + */ + setupFields: function(){ + + // Set up the sections as collapsible + this.$('.style-section-wrapper').each(function(){ + var $s = $(this); + + $s.find('.style-section-head').click( function(e){ + e.preventDefault(); + $s.find('.style-section-fields').slideToggle('fast'); + } ); + }); + + // Set up the color fields + if(typeof $.fn.wpColorPicker != 'undefined') { + this.$('.so-wp-color-field').wpColorPicker(); + } + + // Set up the image select fields + this.$('.style-field-image').each( function(){ + var frame = null; + var $s = $(this); + + $s.find('.so-image-selector').click( function(){ + + if( frame == null ) { + // Create the media frame. + frame = wp.media({ + // Set the title of the modal. + title: 'choose', + + // Tell the modal to show only images. + library: { + type: 'image' + }, + + // Customize the submit button. + button: { + // Set the text of the button. + text: 'Done', + close: true + } + }); + + frame.on( 'select', function(){ + var attachment = frame.state().get('selection').first().attributes; + + try { + $s.find( '.current-image' ).css( 'background-image', 'url(' + attachment.sizes.thumbnail.url + ')' ); + } + catch(e) { + // We'll use the full image instead + $s.find( '.current-image' ).css( 'background-image', 'url(' + attachment.sizes.full.url + ')' ); + } + + // Store the ID + $s.find('input').val( attachment.id ) + } ); + } + + frame.open(); + + } ); + + // Handle clicking on remove + $s.find('.remove-image').click(function(){ + $s.find( '.current-image').css('background-image', 'none'); + $s.find('input').val( '' ) + }); + } ); + + // Set up all the measurement fields + this.$('.style-field-measurement').each(function(){ + var $$ = $(this); + + var text = $$.find('input[type="text"]'); + var unit = $$.find('select'); + var hidden = $$.find('input[type="hidden"]'); + + // Load the value from the hidden field + if( hidden.val() != '' ) { + var re = /([0-9\.,]+)(.*)/; + var match = re.exec( hidden.val() ); + if( match != null && typeof match[1] != 'undefined' && typeof match[2] != 'undefined' ) { + text.val( match[1] ); + unit.val( match[2] ); + } + } + + var setVal = function(){ + hidden.val( text.val() + unit.val() ); + }; + + // Set the value when ever anything changes + text.keyup(setVal).change(setVal); + unit.change(setVal); + } ); + } + + } ); + +} )( jQuery, _, soPanelsOptions ); \ No newline at end of file diff --git a/js/siteorigin-panels.js b/js/siteorigin-panels.js new file mode 100644 index 000000000..0dc1c2bed --- /dev/null +++ b/js/siteorigin-panels.js @@ -0,0 +1,3281 @@ +/** + * Everything we need for SiteOrigin Page Builder. + * + * @copyright Greg Priday 2013 - 2014 - + * @license GPL 3.0 http://www.gnu.org/licenses/gpl.html + */ + +( function( $, _, panelsOptions ){ + + var panels = { + model : { }, + collection : { }, + view : { }, + dialog : { }, + fn : {} + }; + + /** + * Model for an instance of a widget + */ + panels.model.widget = Backbone.Model.extend( { + + cell: null, + + defaults: { + // The PHP Class of the widget + class : null, + + // Is this class missing? + missing : false, + + // The values of the widget + values: {}, + + // Have the current values been passed through the widgets update function + raw: false, + + // Visual style fields + styles: {} + }, + + initialize: function(){ + }, + + /** + * @param field + * @returns {*} + */ + getWidgetField: function(field){ + if(typeof panelsOptions['widgets'][this.get('class')] == 'undefined') { + if(field == 'title' || field == 'description') return panelsOptions.loc.missing_widget[field]; + else return ''; + } + else return panelsOptions['widgets'][this.get('class')][field]; + }, + + /** + * Move this widget to a new cell + * + * @param panels.model.cell newCell + */ + moveToCell: function(newCell){ + if( this.cell.cid == newCell.cid ) return; + + this.cell = newCell; + this.collection.remove(this, {silent:true}); + newCell.widgets.add(this, {silent:true}); + }, + + /** + * Trigger an event on the model that indicates a user wants to edit it + */ + triggerEdit: function(){ + this.trigger('user_edit', this); + }, + + /** + * Trigger an event on the widget that indicates a user wants to duplicate it + */ + triggerDuplicate: function(){ + this.trigger('user_duplicate', this); + }, + + /** + * This is basically a wrapper for set that checks if we need to trigger a change + */ + setValues: function(values){ + var hasChanged = false; + if( JSON.stringify( values ) != JSON.stringify( this.get('values') ) ) { + hasChanged = true; + } + + this.set( 'values', values, {silent: true} ); + + if( hasChanged ) { + // We'll trigger our own change events + this.trigger('change'); + this.trigger('change:values') + } + }, + + /** + * Create a clone of this widget attached to the given cell. + * + * @param {panels.model.cell} cell + * @returns {panels.model.widget} + */ + clone: function( cell, options ){ + if( typeof cell == 'undefined' ) cell = this.cell; + + var clone = new this.constructor( this.attributes ); + + // Create a deep clone of the original values + var cloneValues = JSON.parse( JSON.stringify( this.get('values') ) ); + + if( this.get('class') == "SiteOrigin_Panels_Widgets_Layout" ) { + // Special case of this being a layout widget, it needs a new ID + cloneValues.builder_id = Math.random().toString(36).substr(2); + } + + clone.set( 'values', cloneValues, { silent: true } ); + clone.set( 'collection', cell.widgets, { silent: true } ); + clone.cell = cell; + clone.isDuplicate = true; + return clone; + }, + + /** + * Gets the value that makes most sense as the title. + */ + getTitle: function(){ + var widgetData = panelsOptions.widgets[this.get('class')]; + if( typeof widgetData.panels_title != 'undefined' ) { + // This means that the widget has told us which field it wants us to use as a title + if( widgetData.panels_title === false ) { + return panelsOptions.widgets[this.get('class')].description; + } + } + + var values = this.get('values'); + var thisModel = this; + + // Create a list of fields to check for a title + var titleFields = ['title', 'text']; + for (var k in values){ + titleFields.push( k ); + } + titleFields = _.uniq(titleFields); + + for( var i in titleFields ) { + if( + typeof values[titleFields[i]] != 'undefined' && + typeof values[titleFields[i]] == 'string' && + values[titleFields[i]] != '' && + !$.isNumeric( values[titleFields[i]] ) + ) { + var title = values[ titleFields[i] ]; + title = title.replace(/<\/?[^>]+(>|$)/g, ""); + var parts = title.split(" "); + parts = parts.slice(0, 20); + return parts.join(' '); + } + } + + // If we still have nothing, then just return the widget description + return this.getWidgetField('description'); + } + + } ); + + /** + * The view for a widget in the builder interface + */ + panels.view.widget = Backbone.View.extend({ + template: _.template( $('#siteorigin-panels-builder-widget').html() ), + + // The cell view that + cell: null, + + dialog: null, + + events: { + 'click .widget-edit' : 'editHandler', + 'click .title h4' : 'editHandler', + 'click .actions .widget-duplicate' : 'duplicateHandler', + 'click .actions .widget-delete' : 'deleteHandler' + }, + + /** + * Initialize the widget + */ + initialize: function(){ + // The 2 user actions on the model that this view will handle. + this.model.on('user_edit', this.editHandler, this); + this.model.on('user_duplicate', this.duplicateHandler, this); + this.model.on('destroy', this.onModelDestroy, this); + this.model.on('visual_destroy', this.visualDestroyModel, this); + + this.model.on('change:values', this.onModelChange, this); + }, + + /** + * Render the widget + */ + render: function(options){ + options = _.extend({'loadForm': false}, options); + + this.setElement( this.template( { + title : this.model.getWidgetField('title'), + description : this.model.getTitle() + } ) ); + + this.$el.data( 'view', this ); + + if( _.size( this.model.get('values') ) == 0 || options.loadForm) { + // If this widget doesn't have a value, create a form and save it + var dialog = this.getEditDialog(); + + // Save the widget as soon as the form is loaded + dialog.once('form_loaded', dialog.saveWidget, dialog); + + // Setup the dialog to load the form + dialog.setupDialog(); + } + }, + + /** + * Display an animation that implies creation using a visual animation + */ + visualCreate: function(){ + this.$el.hide().fadeIn( 'fast' ); + }, + + /** + * Get the dialog view of the form that edits this widget + * + * @returns {null} + */ + getEditDialog: function(){ + if(this.dialog == null){ + this.dialog = new panels.dialog.widget({ + model: this.model + }); + this.dialog.setBuilder(this.cell.row.builder); + + // Store the widget view + this.dialog.widgetView = this; + } + return this.dialog; + }, + + /** + * Handle clicking on edit widget. + * + * @returns {boolean} + */ + editHandler: function(){ + // Create a new dialog for editing this + this.getEditDialog().openDialog(); + return false; + }, + + /** + * Handle clicking on duplicate. + * + * @returns {boolean} + */ + duplicateHandler: function(){ + // Add the history entry + this.cell.row.builder.addHistoryEntry('widget_duplicated'); + + // Create the new widget and connect it to the widget collection for the current row + var newWidget = this.model.clone( this.model.cell ); + + this.cell.model.widgets.add(newWidget, { + // Add this after the existing model + at: this.model.collection.indexOf( this.model ) + 1 + }); + + return false; + }, + + /** + * Handle clicking on delete. + * + * @returns {boolean} + */ + deleteHandler: function(){ + this.model.trigger('visual_destroy'); + return false; + }, + + onModelChange: function(){ + // Update the description when ever the model changes + this.$('.description').html( this.model.getTitle() ); + }, + + /** + * When the model is destroyed, fade it out + */ + onModelDestroy: function(){ + this.remove(); + }, + + /** + * Visually destroy a model + */ + visualDestroyModel: function(){ + // Add the history entry + this.cell.row.builder.addHistoryEntry('widget_deleted'); + + var thisView = this; + this.$el.fadeOut('fast', function(){ + thisView.cell.row.resize(); + thisView.model.destroy(); + } ); + } + + }); + + /** + * A collection of widgets, most often used for cells + */ + panels.collection.widgets = Backbone.Collection.extend( { + model : panels.model.widget, + + initialize: function(){ + } + + } ); + + /** + * A cell is a collection of widget instances + */ + panels.model.cell = Backbone.Model.extend( { + /* A collection of widgets */ + widgets: {}, + + /* The row this model belongs to */ + row: null, + + defaults: { + weight : 0 + }, + + /** + * Set up the cell model + */ + initialize: function(){ + this.widgets = new panels.collection.widgets(); + this.on('destroy', this.onDestroy, this); + }, + + /** + * Triggered when we destroy a cell + */ + onDestroy: function(){ + _.invoke(this.widgets.toArray(), 'destroy'); + this.widgets.reset(); + }, + + /** + * Create a clone of the cell, along with all its widgets + */ + clone: function(row, cloneOptions){ + if( typeof row == 'undefined' ) row = this.row; + cloneOptions = _.extend({ cloneWidgets: true }, cloneOptions); + + var clone = new this.constructor( this.attributes ); + clone.set('collection', row.cells, {silent: true}); + clone.row = row; + + if( cloneOptions.cloneWidgets ) { + // Now we're going add all the widgets that belong to this, to the clone + this.widgets.each(function(widget){ + clone.widgets.add( widget.clone( clone, cloneOptions ), {silent: true} ); + }); + } + + return clone; + } + + } ); + + /** + * A cell collection is used to represent a row + */ + panels.collection.cells = Backbone.Collection.extend( { + model: panels.cell, + + initialize: function(){ + this.on('add', this.onAddCell, this); + }, + + /** + * Get the total weight for the cells in this collection. + * @returns {number} + */ + totalWeight: function(){ + var totalWeight = 0; + this.each(function(cell){ + totalWeight += cell.get('weight'); + }); + + return totalWeight; + } + } ); + + /** + * The view for a cell + */ + panels.view.cell = Backbone.View.extend( { + template: _.template( $('#siteorigin-panels-builder-cell').html() ), + events : { + 'click .cell-wrapper' : 'handleCellClick', + 'click .so-cell-actions a' : 'handleActionClick' + }, + + /* The row view that this cell is a part of */ + row: null, + widgetSortable: null, + + initialize: function(){ + this.model.widgets.on('add', this.onAddWidget, this); + }, + + /** + * Render the actual cell + */ + render: function(){ + var templateArgs = { + weight: this.model.get('weight'), + totalWeight: this.row.model.cells.totalWeight() + }; + + this.setElement( this.template(templateArgs) ); + this.$el.data('view', this); + + // Now lets render any widgets that are currently in the row + var thisView = this; + this.model.widgets.each(function(widget){ + var widgetView = new panels.view.widget( { model: widget } ); + widgetView.cell = thisView; + widgetView.render(); + + widgetView.$el.appendTo( thisView.$('.widgets-container') ); + }); + + this.initSortable(); + this.initResizable(); + }, + + /** + * Initialize the widget sortable + */ + initSortable: function(){ + var cellView = this; + + var builderID = cellView.row.builder.$el.attr('id'); + + var scrollTop; + + // Create a widget sortable that's connected with all other cells + this.widgetSortable = this.$el.find('.widgets-container').sortable( { + placeholder: "so-widget-sortable-highlight", + connectWith: '#' + builderID + ' .so-cells .cell .widgets-container', + tolerance:'pointer', + scroll: false, + over: function(e, ui){ + // This will make all the rows in the current builder resize + cellView.row.builder.trigger('widget_sortable_move'); + }, + stop: function(e, ui){ + cellView.row.builder.addHistoryEntry('widget_moved'); + + var widget = $(ui.item).data('view'); + var targetCell = $(ui.item).closest('.cell').data('view'); + + // Move the model and the view to the new cell + widget.model.moveToCell( targetCell.model ); + widget.cell = targetCell; + + cellView.row.builder.sortCollections(); + }, + helper: function(e, el){ + var helper = el.clone().css('width', el.outerWidth()).addClass('widget-being-dragged').appendTo( 'body' ); + + // Center the helper to the mouse cursor. + if( el.outerWidth() > 720 ) { + helper.animate({ + 'margin-left': e.pageX - el.offset().left - (480 / 2), + 'width': 480 + }, 'fast'); + } + + return helper; + } + } ); + }, + + /** + * Refresh the widget sortable when a new widget is added + */ + refreshSortable: function(){ + this.widgetSortable.sortable('refresh'); + }, + + /** + * This will make the cell resizble + */ + initResizable: function(){ + // var neighbor = this.$el.previous().data('view'); + var handle = this.$('.resize-handle').css('position', 'absolute'); + var container = this.row.$el; + var cellView = this; + + // The view of the cell to the left is stored when dragging starts. + var previousCell; + + handle.draggable({ + axis: 'x', + containment: container, + start: function(e, ui){ + // Set the containment to the cell parent + previousCell = cellView.$el.prev().data('view'); + if( typeof previousCell == 'undefined' ) return false; + + // Create the clone for the current cell + var newCellClone = cellView.$el.clone().appendTo(ui.helper).css({ + position : 'absolute', + top : '0', + width : cellView.$el.outerWidth(), + left : 5, + height: cellView.$el.outerHeight() + }); + newCellClone.find('.resize-handle').remove(); + + // Create the clone for the previous cell + var prevCellClone = previousCell.$el.clone().appendTo(ui.helper).css({ + position : 'absolute', + top : '0', + width : previousCell.$el.outerWidth(), + right : 5, + height: previousCell.$el.outerHeight() + }); + prevCellClone.find('.resize-handle').remove(); + + $(this).data({ + 'newCellClone' : newCellClone, + 'prevCellClone' : prevCellClone + }); + }, + drag: function(e, ui){ + // Calculate the new cell and previous cell widths as a percent + var containerWidth = cellView.row.$el.width() + 10; + var ncw = cellView.model.get('weight') - ( ( ui.position.left + handle.outerWidth()/2 ) / containerWidth ); + var pcw = previousCell.model.get('weight') + ( ( ui.position.left + handle.outerWidth()/2 ) / containerWidth ); + + $(this).data('newCellClone').css('width', containerWidth * ncw ) + .find('.preview-cell-weight').html( Math.round(ncw*1000)/10 ); + + $(this).data('prevCellClone').css('width', containerWidth * pcw ) + .find('.preview-cell-weight').html( Math.round(pcw*1000)/10 ); + }, + stop: function(e, ui){ + // Remove the clones + $(this).data('newCellClone').remove(); + $(this).data('prevCellClone').remove(); + + var containerWidth = cellView.row.$el.width() + 10; + var ncw = cellView.model.get('weight') - ( ( ui.position.left + handle.outerWidth()/2 ) / containerWidth ); + var pcw = previousCell.model.get('weight') + ( ( ui.position.left + handle.outerWidth()/2 ) / containerWidth ); + + if( ncw > 0.02 && pcw > 0.02 ) { + cellView.row.builder.addHistoryEntry('cell_resized'); + cellView.model.set('weight', ncw); + previousCell.model.set('weight', pcw); + cellView.row.resize(); + } + + ui.helper.css('left', -handle.outerWidth()/2); + } + }); + + }, + + /** + * This is triggered when ever a widget is added to the row collection. + * + * @param widget + */ + onAddWidget: function(widget, collection, options){ + options = _.extend({noAnimate : false}, options); + + // Create the view for the widget + var view = new panels.view.widget( { + model: widget + } ); + view.cell = this; + + if( typeof widget.isDuplicate == 'undefined' ) { + widget.isDuplicate = false; + } + + // Render and load the form if this is a duplicate + view.render({ + 'loadForm': widget.isDuplicate + }); + + if( typeof options.at == 'undefined' || collection.length <= 1 ) { + // Insert this at the end of the widgets container + view.$el.appendTo( this.$( '.widgets-container' ) ); + } + else { + // We need to insert this at a specific position + view.$el.insertAfter( + this.$('.widgets-container .so-widget').eq( options.at - 1 ) + ); + } + + if( options.noAnimate === false ) { + // We need an animation + view.visualCreate(); + } + + this.refreshSortable(); + this.row.resize(); + }, + + /** + * Handle an action click on this cell + * + * @param e + * @returns {boolean} + */ + handleActionClick : function(e){ + return false; + } + } ); + + /** + * Model for a row of cells + */ + panels.model.row = Backbone.Model.extend( { + /* A collection of the cells in this row */ + cells: {}, + + /* The builder model */ + builder: null, + + defaults :{ + style: {} + }, + + /** + * Initialize the row model + */ + initialize: function(){ + this.cells = new panels.collection.cells(); + this.on('destroy', this.onDestroy, this); + }, + + /** + * Add cells to the model row + * + * @param cells an array of cells, where each object in the array has a weight value + * @todo make this handle changing the number of cells in an existing row + */ + setCells: function(cells){ + var thisModel = this; + + if( this.cells.length == 0 ) { + // We're adding the initial cells + _.each(cells, function (cellWeight) { + // Add the new cell to the row + var cell = new panels.model.cell({ + weight: cellWeight, + collection: thisModel.cells + }); + cell.row = thisModel; + thisModel.cells.add(cell); + }); + } + else { + + if(cells.length > this.cells.length) { + // We need to add cells + for( var i = this.cells.length; i < cells.length; i++ ) { + var cell = new panels.model.cell({ + weight: cells[ cells.length + i ], + collection: thisModel.cells + }); + cell.row = this; + thisModel.cells.add(cell); + } + + } + else if(cells.length < this.cells.length) { + // We need to remove cells + _.each(this.cells.slice( cells.length, this.cells.length), function(cell){ + cell.destroy(); + }); + } + + // Now we need to change the weights of all the cells + this.cells.each(function(cell, i){ + cell.set('weight', cells[i]); + }); + } + + // Rescale the cells when we add or remove + this.reweightCells(); + }, + + /** + * Make sure that all the cell weights add up to 1 + */ + reweightCells: function() { + var totalWeight = 0; + this.cells.each( function(cell){ + totalWeight += cell.get('weight'); + } ); + + this.cells.each( function(cell){ + cell.set( 'weight', cell.get('weight') / totalWeight ); + } ); + + // This is for the row view to hook into and resize + this.trigger('reweight_cells'); + }, + + /** + * Triggered when the model is destroyed + */ + onDestroy: function(){ + // Also destroy all the cells + _.invoke(this.cells.toArray(), 'destroy'); + this.cells.reset(); + }, + + /** + * Create a clone of the row, along with all its cells + * + * @param {panels.model.builder} builder The builder model to attach this to. + * + * @return {panels.model.row} The cloned row. + */ + clone: function( builder, cloneOptions ){ + if(typeof builder == 'undefined') builder = this.builder; + cloneOptions = _.extend({ cloneCells: true }, cloneOptions); + + var clone = new this.constructor( this.attributes ); + clone.set('collection', builder.rows, {silent: true}); + clone.builder = builder; + + if( cloneOptions.cloneCells ) { + // Clone all the rows + this.cells.each(function(cell){ + clone.cells.add( cell.clone( clone, cloneOptions ), {silent: true}); + }); + } + + return clone; + } + } ); + + /** + * A collection of rows. This is used to represent the entire content of Page Builder. + */ + panels.collection.rows = Backbone.Collection.extend( { + model: panels.model.row, + + initialize: function(){ + }, + + /** + * Destroy all the rows in this collection + */ + empty: function(){ + var model; + while ( model = this.collection.first() ) { + model.destroy(); + } + } + } ); + + /** + * View for handling the row. + */ + panels.view.row = Backbone.View.extend( { + template: _.template( $('#siteorigin-panels-builder-row').html() ), + + events: { + 'click .so-row-settings' : 'editSettingsHandler', + 'click .so-row-duplicate' : 'duplicateHandler', + 'click .so-row-delete' : 'confirmedDeleteHandler' + }, + + builder: null, + dialog: null, + + /** + * Initialize the row view + */ + initialize: function(){ + + this.model.cells.on('add', this.handleCellAdd, this); + this.model.cells.on('remove', this.handleCellRemove, this); + this.model.on('reweight_cells', this.resize, this); + + this.model.on('destroy', this.onModelDestroy, this); + this.model.on('visual_destroy', this.visualDestroyModel, this); + + var thisView = this; + this.model.cells.each(function(cell){ + thisView.listenTo(cell.widgets, 'add', thisView.resize); + }); + + // When ever a new cell is added, listen to it for new widgets + this.model.cells.on('add', function(cell){ + thisView.listenTo(cell.widgets, 'add', thisView.resize); + }, this); + + }, + + /** + * Render the row. + * + * @returns {panels.view.row} + */ + render: function(){ + this.setElement( this.template() ); + this.$el.data('view', this); + + // Create views for the cells in this row + var thisView = this; + this.model.cells.each( function(cell){ + var cellView = new panels.view.cell({ + model: cell + }); + cellView.row = thisView; + cellView.render(); + cellView.$el.appendTo( thisView.$('.so-cells') ); + } ); + + // Resize the rows when ever the widget sortable moves + this.builder.on('widget_sortable_move', this.resize, this); + this.builder.on('builder_resize', this.resize, this); + + this.resize(); + + return this; + }, + + /** + * Give a visual indication of the creation of this row + */ + visualCreate: function(){ + this.$el.hide().fadeIn('fast'); + }, + + /** + * Visually resize the row so that all cell heights are the same and the widths so that they balance to 100% + * + * @param e + */ + resize: function(e){ + // Don't resize this + if( !this.$el.is(':visible') ) return; + + // Reset everything to have an automatic height + this.$el.find( '.so-cells .cell-wrapper' ).css( 'min-height', 0 ); + + // We'll tie the values to the row view, to prevent issue with values going to different rows + var height = 0; + this.$el.find('.so-cells .cell').each( function () { + height = Math.max( + height, + $(this ).height() + ); + + $( this ).css( 'width', ( $(this).data('view').model.get('weight') * 100 ) + "%" ); + } ); + + // Resize all the grids and cell wrappers + this.$el.find( '.so-cells .cell-wrapper' ).css( 'min-height', Math.max( height, 70 ) ); + }, + + /** + * Remove the view from the dom. + */ + onModelDestroy: function() { + this.remove(); + }, + + /** + * Fade out the view and destroy the model + */ + visualDestroyModel: function(){ + this.builder.addHistoryEntry('row_deleted'); + var thisView = this; + this.$el.fadeOut('normal', function(){ + thisView.model.destroy(); + thisView.builder.model.refreshPanelsData(); + + if(thisView.builder.liveEditor.displayed) { + thisView.builder.liveEditor.refreshWidgets(); + } + }); + }, + + /** + * Duplicate this row. + * + * @return {boolean} + */ + duplicateHandler: function(){ + this.builder.addHistoryEntry('row_duplicated'); + + var duplicateRow = this.model.clone( this.builder.model ); + + this.builder.model.rows.add( duplicateRow, { + at: this.builder.model.rows.indexOf( this.model ) + 1 + } ); + + return false; + }, + + /** + * Handles deleting the row with a confirmation. + */ + confirmedDeleteHandler: function(e){ + var $$ = $(e.target); + + // The user clicked on the dashicon + if( $$.hasClass('dashicons') ) $$ = $$.parent(); + + if( $$.hasClass('so-confirmed') ) { + this.visualDestroyModel(); + } + else { + var originalText = $$.html(); + + $$.addClass('so-confirmed').html( + '' + panelsOptions.loc.dropdown_confirm + ); + + setTimeout(function(){ + $$.removeClass('so-confirmed').html(originalText); + }, 2500); + } + + return false; + }, + + /** + * Handle displaying the settings dialog + */ + editSettingsHandler: function(){ + // Lets open up an instance of the settings dialog + var dialog = this.builder.dialogs.row; + + if( this.dialog == null ) { + // Create the dialog + this.dialog = new panels.dialog.row(); + this.dialog.setBuilder( this.builder).setRowModel( this.model); + } + + this.dialog.openDialog(); + + return false; + }, + + /** + * Handle deleting this entire row. + */ + deleteHandler: function(){ + this.model.destroy(); + return false; + }, + + /** + * Handle a new cell being added to this row view. For now we'll assume the new cell is always last + */ + handleCellAdd: function(cell){ + var cellView = new panels.view.cell({ + model: cell + }); + cellView.row = this; + cellView.render(); + cellView.$el.appendTo( this.$('.so-cells') ); + }, + + /** + * Handle a cell being removed from this row view + */ + handleCellRemove: function(cell){ + // Find the view that ties in to the cell we're removing + this.$el.find('.so-cells > .cell').each( function(){ + var view = $(this).data('view'); + if(typeof view == 'undefined') return; + + if( view.model.cid == cell.cid ) { + // Remove this view + view.remove(); + } + } ); + } + + } ); + + /** + * The builder model + */ + panels.model.builder = Backbone.Model.extend( { + rows: {}, + + defaults : { + 'data' : { + 'widgets' : [], + 'grids' : [], + 'grid_cells' : [] + } + }, + + initialize: function(){ + // These are the main rows in the interface + this.rows = new panels.collection.rows(); + }, + + /** + * Add a new row to this builder. + * + * @param weights + */ + addRow: function( weights, options ){ + options = _.extend({noAnimate : false}, options); + // Create the actual row + var row = new panels.model.row( { + collection: this.rows + } ); + row.setCells( weights ); + row.builder = this; + this.rows.add(row, options); + + return row; + }, + + /** + * Load the panels data into the builder + * + * @param data + */ + loadPanelsData: function(data){ + // Start by destroying any rows that currently exist. This will in turn destroy cells, widgets and all the associated views + this.emptyRows(); + + // This will empty out the current rows and reload the builder data. + this.set( 'data', data, {silent: true} ); + + var cit = 0; + var rows = []; + + if( typeof data.grid_cells == 'undefined' ) return; + + var gi; + for(var ci = 0; ci < data.grid_cells.length; ci++) { + gi = parseInt(data.grid_cells[ci].grid); + if(typeof rows[gi] == 'undefined') rows[gi] = []; + + rows[gi].push( parseFloat( data.grid_cells[ci].weight ) ); + } + + var builderModel = this; + _.each( rows, function(row, i){ + // This will create and add the row model and its cells + var row = builderModel.addRow( row, { noAnimate: true } ); + + if( typeof data.grids[i].style != 'undefined' ) { + row.set( 'style', data.grids[i].style ); + } + } ); + + + if( typeof data.widgets == 'undefined' ) return; + + // Add the widgets + _.each(data.widgets, function(widgetData){ + try { + var panels_info = null; + if (typeof widgetData.panels_info != 'undefined') { + panels_info = widgetData.panels_info; + delete widgetData.panels_info; + } + else { + panels_info = widgetData.info; + delete widgetData.info; + } + + var row = builderModel.rows.at( parseInt(panels_info.grid) ); + var cell = row.cells.at(parseInt(panels_info.cell)); + + var newWidget = new panels.model.widget({ + class: panels_info.class, + values: widgetData + }); + + if( typeof panels_info.style != 'undefined' ) { + newWidget.set('style', panels_info.style ); + } + + newWidget.cell = cell; + cell.widgets.add(newWidget, {noAnimate: true}); + } + catch (err) { + // TODO handle this error + } + } ); + }, + + /** + * Convert the content of the builder into a object that represents the page builder data + */ + getPanelsData: function(){ + + var data = { + 'widgets' : [], + 'grids' : [], + 'grid_cells' : [] + }; + var widgetId = 0; + + this.rows.each(function(row, ri){ + + row.cells.each(function(cell, ci){ + + cell.widgets.each(function(widget, wi){ + // Add the data for the widget, including the panels_info field. + var values = _.extend( _.clone( widget.get('values') ), { + panels_info : { + class: widget.get('class'), + raw: widget.get('raw'), + grid: ri, + cell: ci, + id: widgetId++, + style: widget.get('style') + } + } ); + data.widgets.push( values ); + }); + + // Add the cell info + data.grid_cells.push( { + grid: ri, + weight: cell.get('weight') + } ); + + }); + + data.grids.push( { + cells: row.cells.length, + style: row.get('style') + } ); + + } ); + + return data; + + }, + + /** + * This will check all the current entries and refresh the panels data + */ + refreshPanelsData: function(){ + var oldData = JSON.stringify( this.get('data') ); + var newData = this.getPanelsData(); + this.set( 'data', newData, { silent: true } ); + + if( JSON.stringify( newData ) != oldData ) { + // The default change event doesn't trigger on deep changes, so we'll trigger our own + this.trigger('change'); + this.trigger('change:data'); + } + }, + + /** + * Empty all the rows and the cells/widgets they contain. + */ + emptyRows: function(){ + _.invoke(this.rows.toArray(), 'destroy'); + this.rows.reset(); + + return this; + } + + } ); + + /** + * This is the main view for the Page Builder interface. + */ + panels.view.builder = Backbone.View.extend( { + template: _.template( $('#siteorigin-panels-builder').html() ), + dialogs: { }, + rowsSortable: null, + dataField : false, + currentData: '', + + attachedToEditor: false, + liveEditor: false, + + events: { + 'click .so-tool-button.so-widget-add': 'displayAddWidgetDialog', + 'click .so-tool-button.so-row-add': 'displayAddRowDialog', + 'click .so-tool-button.so-prebuilt-add': 'displayAddPrebuiltDialog', + 'click .so-tool-button.so-history': 'displayHistoryDialog', + 'click .so-tool-button.so-live-editor': 'displayLiveEditor', + + 'click .so-cells .cell .cell-wrapper' : 'cellClickHandler' + }, + + /* A row collection */ + rows: null, + + /** + * Initialize the builder + */ + initialize: function(){ + var builder = this; + + // Now lets create all the dialog boxes that the main builder interface uses + this.dialogs = { + widgets: new panels.dialog.widgets(), + row: new panels.dialog.row(), + prebuilt: new panels.dialog.prebuilt() + }; + + // Set the builder for each dialog and render it. + _.each(this.dialogs, function(p, i, d){ + d[i].setBuilder( builder ); + }) + + this.dialogs.row.setRowDialogType('create'); + + // This handles a new row being added to the collection - we'll display it in the interface + this.model.rows.on('add', this.onAddRow, this); + + // Reflow the entire builder when ever the + $(window).resize(function(e){ + if(e.target == window) { + builder.trigger('builder_resize'); + } + }); + + // When the data changes in the model, store it in the field + this.model.on('change:data', this.storeModelData, this); + + // Handle a content change + this.on('content_change', this.handleContentChange, this); + + this.on('display_builder', this.handleDisplayBuilder, this); + + this.model.on('change:data', this.toggleWelcomeDisplay, this); + }, + + /** + * Render the builder interface. + * + * @return {siteoriginPanels.view.builder} + */ + render: function(){ + this.$el.html( this.template() ); + this.$el + .attr( 'id', 'siteorigin-panels-builder-' + this.cid ) + .addClass('so-builder-container'); + return this; + }, + + /** + * Attach the builder to the given container + * + * @param container + * @returns {panels.view.builder} + */ + attach: function(options) { + + options = _.extend({ container: false, dialog: false }, options); + + if( options.dialog ) { + // We're going to add this to a dialog + this.dialog = new panels.dialog.builder(); + this.dialog.builder = this; + } + else { + // Attach this in the standard way + this.$el.appendTo( options.container ); + this.metabox = options.container.closest('.postbox'); + this.initSortable(); + } + + return this; + }, + + /** + * This will move the Page Builder Metabox into the editor + * + * @returns {panels.view.builder} + */ + attachToEditor: function(){ + if( typeof this.metabox == 'undefined' ) return this; + + this.attachedToEditor = true; + var metabox = this.metabox; + var thisView = this; + + // Handle switching between the page builder and other tabs + $( '#wp-content-wrap .wp-editor-tabs' ) + .find( '.wp-switch-editor' ) + .click(function (e) { + e.preventDefault(); + $( '#wp-content-editor-container, #post-status-info' ).show(); + metabox.hide(); + $( '#wp-content-wrap' ).removeClass('panels-active'); + $('#content-resize-handle' ).show(); + thisView.trigger('hide_builder'); + } ).end() + .prepend( + $( '' + metabox.find( 'h3.hndle span' ).html() + '' ) + .click( function (e) { + // Switch to the Page Builder interface + e.preventDefault(); + + var $$ = $( this ); + + // Hide the standard content editor + $( '#wp-content-wrap, #post-status-info' ).hide(); + + // Show page builder and the inside div + metabox.show().find('> .inside').show(); + + // Triggers full refresh + $( window ).resize(); + $( document).scroll(); + + thisView.trigger('display_builder'); + } ) + ); + + // Switch back to the standard editor + metabox.find('.so-switch-to-standard').click(function(e){ + e.preventDefault(); + + // Switch back to the standard editor + $( '#wp-content-wrap, #post-status-info' ).show(); + metabox.hide(); + // Resize to trigger reflow of WordPress editor stuff + $( window ).resize(); + }).show(); + + // Move the panels box into a tab of the content editor + metabox.insertAfter( '#wp-content-wrap').hide().addClass('attached-to-editor'); + + // Switch to the Page Builder interface as soon as we load the page if there are widgets + var data = this.model.get('data'); + if( typeof data.widgets != 'undefined' && _.size(data.widgets) != 0 ) { + $('#content-panels.switch-panels').click(); + } + + // We will also make this sticky if its attached to an editor. + var stickToolbar = function(){ + var toolbar = thisView.$('.so-builder-toolbar'); + var newTop = $(window).scrollTop() - thisView.$el.offset().top; + + if( $('#wpadminbar').css('position') == 'fixed' ) { + newTop += $('#wpadminbar').outerHeight(); + } + + // Make sure this falls in an acceptible range. + newTop = Math.max( newTop, 0 ); + newTop = Math.min( newTop, thisView.$el.outerHeight() - toolbar.outerHeight() + 20 ); // 20px extra to account for padding. + + // Position the toolbar + toolbar.css('top', newTop); + thisView.$el.css('padding-top', toolbar.outerHeight()); + } + $( window ).resize( stickToolbar ); + $( document ).scroll( stickToolbar ); + stickToolbar(); + + return this; + }, + + /** + * Initialize the row sortables + */ + initSortable: function(){ + // Create the sortable for the rows + var $el = this.$el; + var builderView = this; + + this.rowsSortable = this.$el.find('.so-rows-container').sortable( { + appendTo: '#wpwrap', + items: '.so-row-container', + handle: '.so-row-move', + tolerance: 'pointer', + scroll: false, + stop: function (e) { + builderView.addHistoryEntry('row_moved'); + + // Sort the rows collection after updating all the indexes. + builderView.sortCollections(); + } + } ); + }, + + /** + * Refresh the row sortable + */ + refreshSortable: function(){ + // Refresh the sortable to account for the new row + if(this.rowsSortable != null) { + this.rowsSortable.sortable('refresh'); + } + }, + + /** + * Set the field that's used to store the data + * @param field + */ + setDataField: function(field, options){ + options = _.extend({ + load: true + }, options); + + this.dataField = field; + this.dataField.data('builder', this); + + if( options.load && field.val() != '') { + var data; + try { + data = JSON.parse( this.dataField.val( ) ); + } + catch(err) { data = '' } + + this.model.loadPanelsData(data); + this.currentData = data; + this.toggleWelcomeDisplay(); + } + + return this; + }, + + /** + * Store the model data in the data field set in this.setDataField. + */ + storeModelData: function(){ + var data = JSON.stringify( this.model.get('data' ) ); + + if( $(this.dataField).val( ) != data ) { + // If the data is different, set it and trigger a content_change event + $(this.dataField).val( data ); + this.trigger('content_change'); + } + }, + + onAddRow: function(row, collection, options){ + options = _.extend( {noAnimate: false}, options ); + // Create a view for the row + var rowView = new panels.view.row( { model: row } ); + rowView.builder = this; + rowView.render(); + + // Attach the row elements to this builder + if( typeof options.at == 'undefined' || collection.length <= 1 ) { + // Insert this at the end of the widgets container + rowView.$el.appendTo( this.$( '.so-rows-container' ) ); + } + else { + // We need to insert this at a specific position + rowView.$el.insertAfter( + this.$('.so-rows-container .so-row-container').eq( options.at - 1 ) + ); + } + + if(options.noAnimate === false) { + rowView.visualCreate(); + } + + this.refreshSortable(); + rowView.resize(); + }, + + displayAddWidgetDialog: function(){ + this.dialogs.widgets.openDialog(); + return false; + }, + + displayAddRowDialog: function(){ + this.dialogs.row.openDialog(); + this.dialogs.row.setRowModel(); // Set this to an empty row model + return false; + }, + + displayAddPrebuiltDialog: function(){ + this.dialogs.prebuilt.openDialog(); + return false; + }, + + displayHistoryDialog: function(){ + this.dialogs.history.openDialog(); + return false; + }, + + cellClickHandler: function(e){ + var cells = this.$el.find('.so-cells .cell').removeClass('cell-selected'); + $(e.target).parent().addClass('cell-selected'); + }, + + /** + * Get the model for the currently active cell + */ + getActiveCell: function(){ + if( this.$('.so-cells .cell').length == 0 ) { + // Create a row with a single cell + this.model.addRow( [1], {noAnimate: true} ); + } + + var activeCell = this.$('.so-cells .cell.cell-selected'); + + if(!activeCell.length) { + activeCell = this.$('.so-cells .cell').first(); + } + + return activeCell.data('view').model; + }, + + /** + * Sort all widget and row collections based on their dom position + */ + sortCollections: function(){ + // Create an array that stores model indexes within the array + var indexes = {}; + + this.$('.so-rows-container .so-row-container').each(function(ri, el){ + var $r = $(el); + indexes[ $r.data('view').model.cid ] = ri; + + $r.find('.so-cells .cell').each(function(ci, el){ + var $c = $(el); + + $c.find('.so-widget').each(function(wi, el) { + var $w = $(el); + indexes[ $w.data('view').model.cid ] = wi; + }) + }); + }); + + // Sort everything + this.model.rows.models = this.model.rows.sortBy(function(model){ + return indexes[model.cid]; + }); + + this.model.rows.each(function(row){ + row.cells.each(function(cell){ + cell.widgets.models = cell.widgets.sortBy(function(widget){ + return indexes[widget.cid]; + }); + }) + }); + + // Update the builder model to reflect the newly ordered data. + this.model.refreshPanelsData(); + }, + + /** + * Add a live editor + * + * @returns {panels.view.builder} + */ + addLiveEditor: function(postId){ + if( typeof panels.view.liveEditor == 'undefined' ) return this; + + // Create the live editor and set the builder to this. + this.liveEditor = new panels.view.liveEditor(); + this.liveEditor.setPostId(postId); + + this.liveEditor.builder = this; + + // Display the live editor button in the toolbar + if( this.liveEditor.hasPreviewUrl() ) { + this.$('.so-builder-toolbar .so-live-editor').show(); + } + + return this; + }, + + /** + * Show the current live editor + */ + displayLiveEditor: function(){ + if(typeof this.liveEditor == 'undefined') return false; + + this.liveEditor.open(); + return false; + }, + + /** + * Add the history browser. + * + * @return {panels.view.builder} + */ + addHistoryBrowser: function(){ + if(typeof panels.dialog.history == 'undefined') return this; + + this.dialogs.history = new panels.dialog.history(); + this.dialogs.history.builder = this; + this.dialogs.history.entries.builder = this.model; + + // Set the revert entry + this.dialogs.history.setRevertEntry( this.model ); + + // Display the live editor button in the toolbar + this.$('.so-builder-toolbar .so-history').show(); + }, + + /** + * Add an entry. + * + * @param text + * @param data + */ + addHistoryEntry: function(text, data){ + if(typeof data == 'undefined') data = null; + + if( typeof this.dialogs.history != 'undefined' ) { + this.dialogs.history.entries.addEntry(text, data); + } + }, + + /** + * Handle a change of the content + */ + handleContentChange: function(){ + + if(this.attachedToEditor) { + // We're going to create a copy of page builder content into the post content + $.post( + ajaxurl, + { + action: 'so_panels_builder_content', + panels_data: JSON.stringify( this.model.getPanelsData() ), + post_id : $('#post_ID').val() + }, + function(content){ + + // Strip all the known layout divs + var t = $('
').html( content ); + t.find( 'div').each(function() { + var c = $(this).contents(); + $(this).replaceWith(c); + }); + content = t.html(); + + // Set the content of the editor + if( typeof tinyMCE == 'undefined' || tinyMCE.get("content") == null ) $('#content').val( content ); + else tinyMCE.get("content").setContent(content); + + // Trigger a focusout (mainly for Yoast SEO) + $('#content').focusout(); + } + ); + } + + if( this.liveEditor !== false ) { + // Refresh the content of the builder + this.liveEditor.refreshPreview(); + } + }, + + /** + * Handle displaying the builder + */ + handleDisplayBuilder: function(){ + var editorContent = ''; + if ( typeof tinyMCE != 'undefined' ) editor = tinyMCE.get( 'content' ); + if( editor != null && typeof( editor.getContent ) == "function" ) { + editorContent = editor.getContent(); + } + else { + editorContent = $('textarea#content').val(); + } + + if( this.model.get('data') == '' && editorContent != '') { + // Confirm with the user first + if( !confirm( panelsOptions.loc.confirm_use_builder ) ) return; + + var widgetClass = ''; + if( typeof panelsOptions.widgets["WP_Widget_Black_Studio_TinyMCE"] ) { + widgetClass = 'WP_Widget_Black_Studio_TinyMCE'; + } + // There is a small chance a theme will have removed this, so check + else if( typeof panelsOptions.widgets["WP_Widget_Text"] ) { + widgetClass = 'WP_Widget_Text'; + } + + if( widgetClass == '' ) return; + + // Create the existing page content in a single widget + this.model.loadPanelsData( { + grid_cells : [ { grid: 0, weight: 1 } ], + grids: [ { cells: 1 } ], + widgets: [{ + filter: "1", + text: editorContent, + title: "", + type: "visual", + panels_info: { + class: widgetClass, + raw: false, + grid: 0, + cell: 0 + } + }] + } ); + this.model.trigger('change'); + this.model.trigger('change:data'); + } + else if ( this.model.get('data') == '' ) { + // Set up a blank single row + //this.model.loadPanelsData( { + // grid_cells : [ { grid: 0, weight: 1 } ], + // grids: [ { cells: 1 } ], + // widgets: [] + //} ); + } + + }, + + /** + * Set the parent dialog for all the dialogs in this builder. + * + * @param text + * @param dialog + */ + setDialogParents: function(text, dialog){ + _.each(this.dialogs, function(p, i, d){ + d[i].setParent(text, dialog ); + }); + + // For any future dialogs + this.on('add_dialog', function(newDialog){ + newDialog.setParent(text, dialog); + }, this) + }, + + toggleWelcomeDisplay: function(){ + if( this.model.rows.length ) { + this.$('.so-panels-welcome-message').hide(); + } + else { + this.$('.so-panels-welcome-message').show(); + } + }, + + } ); + + /** + * The default dialog view. This should be extended by the other views. + */ + panels.view.dialog = Backbone.View.extend( { + dialogTemplate: _.template( $('#siteorigin-panels-dialog').html() ), + dialogTabTemplate: _.template( $('#siteorigin-panels-dialog-tab').html() ), + + tabbed: false, + rendered: false, + builder: false, + className: 'so-panels-dialog-wrapper', + dialogClass: '', + parentDialog: false, + + events : { + 'click .so-close': 'closeDialog', + 'click .so-nav.so-previous': 'navToPrevious', + 'click .so-nav.so-next': 'navToNext' + }, + + initialize: function(){ + // The first time this dialog is opened, render it + this.once('open_dialog', this.render); + this.once('open_dialog', this.attach); + this.once('open_dialog', this.setDialogClass); + + this.trigger('initialize_dialog', this); + + if(typeof this.initializeDialog != 'undefined') { + this.initializeDialog(); + } + }, + + /** + * Returns the next dialog in the sequence. Should be overwritten by a child dialog. + * @returns {null} + */ + getNextDialog: function(){ + return null; + }, + + /** + * Returns the previous dialog in this sequence. Should be overwritten by child dialog. + * @returns {null} + */ + getPrevDialog: function(){ + return null; + }, + + /** + * Adds a dialog class to uniquely identify this dialog type + */ + setDialogClass: function(){ + if(this.dialogClass != ''){ + this.$('.so-panels-dialog').addClass(this.dialogClass); + } + }, + + /** + * Set the builder that controls this dialog. + * @param {panels.view.builder} builder + */ + setBuilder: function(builder){ + this.builder = builder; + + // Trigger an add dialog event on the builder so it can modify the dialog in any way + builder.trigger('add_dialog', this, this.builder); + + return this; + }, + + /** + * Attach the dialog to the window + */ + attach: function(){ + this.$el.appendTo( 'body' ); + + return this; + }, + + /** + * Converts an HTML representation of the dialog into arguments for a dialog box + * @param html HTML for the dialog + * @param args Arguments passed to the template + * @returns {} + */ + parseDialogContent: function(html, args){ + // Add a CID + args = _.extend({cid: this.cid}, args); + + + var c = $( ( _.template(html) )( args ) ); + var r = { + title : c.find('.title').html(), + buttons : c.find('.buttons').html(), + content : c.find('.content').html() + }; + + if( c.has('.left-sidebar') ){ + r.left_sidebar = c.find('.left-sidebar').html(); + } + + if( c.has('.right-sidebar') ){ + r.right_sidebar = c.find('.right-sidebar').html(); + } + + return r; + + }, + + /** + * Render the dialog and initialize the tabs + * + * @param attributes + * @returns {panels.view.dialog} + */ + renderDialog: function(attributes){ + this.$el.html( this.dialogTemplate( attributes ) ).hide(); + this.$el.data('view', this); + this.$el.addClass('so-panels-dialog-wrapper'); + + if( this.parentDialog != false ) { + // Add a link to the parent dialog as a sort of crumbtrail. + var thisDialog = this; + var dialogParent = $('').html( this.parentDialog.text + '
' ); + dialogParent.click(function(e){ + e.preventDefault(); + thisDialog.closeDialog(); + thisDialog.parentDialog.openDialog(); + }); + this.$('.so-title-bar').prepend( dialogParent ); + } + + return this; + }, + + /** + * Initialize the sidebar tabs + */ + initTabs: function(){ + var tabs = this.$el.find('.so-sidebar-tabs li a'); + + if(tabs.length == 0) return; + + var thisDialog = this; + tabs.click(function(e){ + e.preventDefault(); + var $$ = $(this); + + thisDialog.$('.so-sidebar-tabs li').removeClass('tab-active'); + thisDialog.$('.so-content .so-content-tabs > *').hide(); + + $$.parent().addClass('tab-active'); + + var url = $$.attr('href'); + if(typeof url != 'undefined' && url.charAt(0) == '#') { + // Display the new tab + var tabName = url.split('#')[1]; + thisDialog.$('.so-content .so-content-tabs .tab-' + tabName).show(); + } + + // This lets other dialogs implement their own custom handlers + thisDialog.trigger('tab_click', $$); + + }); + + // Trigger a click on the first tab + this.$el.find('.so-sidebar-tabs li a').first().click(); + + }, + + /** + * Quickly setup the dialog by opening and closing it. + */ + setupDialog: function(){ + this.openDialog(); + this.closeDialog(); + }, + + /** + * Refresh the next and previous buttons. + */ + refreshDialogNav: function(){ + this.$('.so-title-bar .so-nav').show().removeClass('so-disabled'); + + // Lets also hide the next and previous if we don't have a next and previous dialog + var nextDialog = this.getNextDialog(); + var nextButton = this.$('.so-title-bar .so-next'); + + var prevDialog = this.getPrevDialog(); + var prevButton = this.$('.so-title-bar .so-previous'); + + if(nextDialog === null) nextButton.hide(); + else if(nextDialog === false) nextButton.addClass('so-disabled'); + + if(prevDialog === null) prevButton.hide(); + else if(prevDialog === false) prevButton.addClass('so-disabled'); + }, + + /** + * Open the dialog + */ + openDialog: function(){ + this.trigger('open_dialog'); + + this.refreshDialogNav(); + + // Stop scrolling for the main body + this.bodyScrollTop = $('body').scrollTop(); + $('body').css({'overflow':'hidden'}); + + this.$el.show(); + + // This triggers once everything is visible + this.trigger('open_dialog_complete'); + }, + + /** + * Close the dialog + * + * @param e + * @returns {boolean} + */ + closeDialog: function(e){ + this.trigger('close_dialog'); + + // In the builder, trigger an update + if(typeof this.builder != 'undefined') { + // Store the model data when a dialog is closed. + this.builder.model.refreshPanelsData(); + } + + this.$el.hide(); + + if( !$('.so-panels-dialog-wrapper').is(':visible') ){ + // Restore scrolling to the main body if there are no more dialogs + $('body').css({'overflow':'auto'}); + $('body').scrollTop( this.bodyScrollTop ); + } + + // This triggers once everything is hidden + this.trigger('close_dialog_complete'); + + return false; + }, + + /** + * Navigate to the previous dialog + */ + navToPrevious: function(){ + this.closeDialog(null); + + var prev = this.getPrevDialog(); + if(prev != null && prev != false){ + prev.openDialog(); + } + }, + + /** + * Navigate to the next dialog + */ + navToNext: function(){ + this.closeDialog(null); + + var next = this.getNextDialog(); + if(next != null && next != false){ + next.openDialog(); + } + }, + + /** + * Get the values from the form and convert them into a data array + */ + getFormValues: function(formSelector){ + if(typeof formSelector == 'undefined') formSelector = '.so-content'; + var $f = this.$(formSelector); + + var data = {}, parts; + + // Find all the named fields in the form + $f.find('[name]').each(function(){ + var $$ = $(this); + + var name = /([A-Za-z_]+)\[(.*)\]/.exec( $$.attr('name') ); + + // Create an array with the parts of the name + if(typeof name[2] == 'undefined') { + parts = $$.attr('name'); + } + else { + parts = name[2].split(']['); + parts.unshift( name[1] ); + } + + parts = parts.map(function(e){ + if( !isNaN(parseFloat(e)) && isFinite(e) ) return parseInt(e); + else return e; + }); + + var sub = data; + var fieldValue = null; + + // First we need to get the value from the field + if( $$.attr('type') == 'checkbox' ){ + if ( $$.is(':checked') ) { + fieldValue = $$.val() != '' ? $$.val() : true; + } + else { + fieldValue = false; + } + } + else if( $$.attr('type') == 'radio' ){ + if ( $$.is(':checked') ) { + fieldValue = $$.val(); + } + else { + //skip over unchecked radios + return; + } + } + else if( $$.prop('tagName') == 'TEXTAREA' && $$.hasClass('wp-editor-area') ){ + // This is a TinyMCE editor, so we'll use the tinyMCE object to get the content + var editor = null; + if ( typeof tinyMCE != 'undefined' ) editor = tinyMCE.get( $$.attr('id') ); + + if( editor != null && typeof( editor.getContent ) == "function" ) { + fieldValue = editor.getContent(); + } + } + else if ( $$.prop('tagName') == 'SELECT' ) { + fieldValue = $$.find('option:selected').val(); + } + if( fieldValue == null ) { + fieldValue = $$.val(); + } + + // Now, we need to filter this value if necessary + if( typeof $$.data('panels-filter') != 'undefined' ) { + switch( $$.data('panels-filter') ) { + case 'json_parse': + // Attempt to parse the JSON value of this field + try { + fieldValue = JSON.parse( fieldValue ); + } + catch(err) { + fieldValue = ''; + } + break; + } + } + + // Now convert this into an array + for(var i = 0; i < parts.length; i++) { + if(i == parts.length - 1) { + sub[parts[i]] = fieldValue; + } + else { + if(typeof sub[parts[i]] == 'undefined') { + sub[parts[i]] = {}; + } + sub = sub[parts[i]]; + } + } + + }); // End of each through input fields + + return data; + }, + + /** + * Set a status message for the dialog + */ + setStatusMessage: function(message, loading){ + this.$('.so-toolbar .so-status').html( message ); + if( typeof loading != 'undefined' && loading ) { + this.$('.so-toolbar .so-status').addClass('so-panels-loading'); + } + }, + + /** + * Set the parent after. + */ + setParent: function(text, dialog){ + this.parentDialog = { + text: text, + dialog: dialog + } + } + } ); + + /** + * This is the dialog that holds the builder. + */ + panels.dialog.builder = panels.view.dialog.extend( { + dialogClass : 'so-panels-dialog-add-builder', + + render: function(){ + // Render the dialog and attach it to the builder interface + this.renderDialog( this.parseDialogContent( $('#siteorigin-panels-dialog-builder').html(), {} ) ); + this.$('.so-content .siteorigin-panels-builder').append( this.builder.$el ); + }, + + initializeDialog: function(){ + var thisView = this; + this.once('open_dialog_complete', function(){ + thisView.builder.initSortable(); + }); + + this.on('open_dialog_complete', function(){ + thisView.builder.trigger('builder_resize'); + }); + } + } ); + + /** + * The dialog for selecting a widget to add to the page + */ + panels.dialog.widgets = panels.view.dialog.extend( { + + builder: null, + widgetTemplate: _.template( $('#siteorigin-panels-dialog-widgets-widget').html() ), + filter: {}, + + dialogClass : 'so-panels-dialog-add-widget', + + events: { + 'click .so-close': 'closeDialog', + 'click .widget-type' : 'widgetClickHandler', + 'keyup .so-sidebar-search' : 'searchHandler' + }, + + /** + * Initialize the widget adding dialog + */ + initializeDialog: function(){ + + this.on('open_dialog', function(){ + this.filter.search = ''; + this.filterWidgets(this.filter); + }, this); + + this.on('open_dialog_complete', function(){ + // Clear the search and re-filter the widgets when we open the dialog + this.$('.so-sidebar-search').val('').focus(); + }); + + // We'll implement a custom tab click handler + this.on('tab_click', this.tabClickHandler, this); + }, + + render: function(){ + // Render the dialog and attach it to the builder interface + this.renderDialog( this.parseDialogContent( $('#siteorigin-panels-dialog-widgets').html(), {} ) ); + + // Add all the widgets + _.each( panelsOptions.widgets, function( widget ){ + var $w = $( this.widgetTemplate( { + title : widget.title, + description : widget.description + } ) ) ; + + if(typeof widget.icon == 'undefined') widget.icon = 'dashicons dashicons-admin-generic'; + + if( typeof widget.icon != 'undefined' ){ + $('').addClass( widget.icon ).prependTo( $w.find('.widget-type-wrapper') ); + } + + $w.data('class', widget.class).appendTo( this.$el.find('.widget-type-list') ); + }, this ); + + // Add the sidebar tabs + var tabs = this.$el.find('.so-sidebar-tabs'); + _.each(panelsOptions.widget_dialog_tabs, function(tab){ + var $t = $( this.dialogTabTemplate( { 'title' : tab.title } )).data('filter', tab.filter).appendTo( tabs ); + }, this); + + // We'll be using tabs, so initialize them + this.initTabs(); + }, + + /** + * Handle a tab being clicked + */ + tabClickHandler: function($t){ + // Get the filter from the tab, and filter the widgets + this.filter = $t.parent().data('filter'); + if( this.$el.find('.so-sidebar-search').val() != '' ) { + this.filter.search = this.$el.find('.so-sidebar-search').val(); + } + this.filterWidgets(this.filter); + + return false; + }, + + /** + * Handle changes to the search value + */ + searchHandler: function(e){ + this.filter.search = $(e.target).val(); + this.filterWidgets(this.filter); + }, + + /** + * Filter the widgets that we're displaying + * @param filter + */ + filterWidgets: function(filter) { + if (typeof filter == 'undefined') filter = {}; + + if(typeof filter.groups == 'undefined') filter.groups = ''; + + this.$el.find('.widget-type-list .widget-type').each(function(){ + var $$ = $(this), showWidget; + var widgetClass = $$.data('class'); + + var widgetData = ( typeof panelsOptions.widgets[widgetClass] != 'undefined' ) ? panelsOptions.widgets[widgetClass] : false; + + if( filter.groups.length == 0 ) { + // This filter doesn't specify groups, so show all + showWidget = true; + } + else if( widgetData !== false && _.intersection(filter.groups, panelsOptions.widgets[widgetClass].groups).length ) { + // This widget is in the filter group + showWidget = true; + } + else { + // This widget is not in the filter group + showWidget = false; + } + + // This can probably be done with a more intelligent operator + if( showWidget ) { + + if( typeof filter.search != 'undefined' && filter.search != '' ) { + // Check if the widget title contains the search term + if( widgetData.title.toLowerCase().indexOf( filter.search.toLowerCase() ) == -1 ) { + showWidget = false; + } + } + + } + + if(showWidget) $$.show(); + else $$.hide(); + }); + }, + + /** + * Add the widget to the current builder + * + * @param e + */ + widgetClickHandler : function(e){ + // Add the history entry + this.builder.addHistoryEntry('widget_added'); + + var $w = $(e.currentTarget); + + var widget = new panels.model.widget( { + class: $w.data('class') + } ); + + // Add the widget to the cell model + widget.cell = this.builder.getActiveCell(); + widget.cell.widgets.add( widget ); + + this.closeDialog(); + } + } ); + + /** + * Dialog for displaying a single widget form + */ + panels.dialog.widget = panels.view.dialog.extend( { + + builder: null, + sidebarWidgetTemplate: _.template( $('#siteorigin-panels-dialog-widget-sidebar-widget').html() ), + dialogClass : 'so-panels-dialog-edit-widget', + widgetView : false, + + events: { + 'click .so-close': 'saveHistory', + 'click .so-nav.so-previous': 'navToPrevious', + 'click .so-nav.so-next': 'navToNext', + + // Action handlers + 'click .so-toolbar .so-delete': 'deleteHandler', + 'click .so-toolbar .so-duplicate': 'duplicateHandler' + }, + + initializeDialog: function(){ + this.model.on('destroy', this.remove, this); + }, + + /** + * Render the widget dialog. + */ + render: function() { + // Render the dialog and attach it to the builder interface + this.renderDialog( this.parseDialogContent( $('#siteorigin-panels-dialog-widget').html(), {} ) ); + this.loadForm(); + + if( typeof panelsOptions.widgets[ this.model.get('class') ] != 'undefined') { + this.$('.so-title .widget-name').html( panelsOptions.widgets[ this.model.get('class')].title ); + } + else { + this.$('.so-title .widget-name').html( panelsOptions.loc.missing_widget.title ); + } + + // Now we need to attach the style window + this.styles = new panels.view.styles(); + this.styles.model = this.model; + this.styles.render( 'widget' ); + this.styles.attach( this.$('.so-sidebar.so-right-sidebar') ); + + // Handle the loading class + this.styles.on('styles_loaded', function(){ + this.$('.so-sidebar.so-right-sidebar').removeClass('so-panels-loading'); + }, this); + this.$('.so-sidebar.so-right-sidebar').addClass('so-panels-loading'); + }, + + /** + * Get the previous widget editing dialog by looking at the dom. + * @returns {*} + */ + getPrevDialog: function(){ + var widgets = this.builder.$('.so-cells .cell .so-widget'); + if(widgets.length <= 1) return false; + var currentIndex = widgets.index( this.widgetView.$el ); + + if( currentIndex == 0 ) { + return false; + } + else { + var widgetView = widgets.eq(currentIndex - 1).data('view'); + if(typeof widgetView == 'undefined') return false; + + return widgetView.getEditDialog(); + } + }, + + /** + * Get the next widget editing dialog by looking at the dom. + * @returns {*} + */ + getNextDialog: function(){ + var widgets = this.builder.$('.so-cells .cell .so-widget'); + if(widgets.length <= 1) return false; + var currentIndex = widgets.index( this.widgetView.$el ); + + if( currentIndex == widgets.length - 1 ) { + return false; + } + else { + var widgetView = widgets.eq(currentIndex + 1).data('view'); + if(typeof widgetView == 'undefined') return false; + + return widgetView.getEditDialog(); + } + }, + + /** + * Load the widget form from the server + */ + loadForm: function(){ + var thisView = this; + this.$el.find('.so-content').addClass('so-panels-loading'); + + var data = { + 'action' : 'so_panels_widget_form', + 'widget' : this.model.get('class'), + 'instance' : JSON.stringify( this.model.get('values') ), + 'raw' : this.model.get('raw') + }; + + $.post( + ajaxurl, + data, + function(result){ + // Add in the CID of the widget model + var html = result.replace( /\{\$id\}/g, thisView.model.cid ); + + // Load this content into the form + thisView.$el.find('.so-content') + .removeClass('so-panels-loading') + .html(html); + + // Trigger all the necessary events + thisView.trigger('form_loaded', thisView); + + // For legacy compatibility, trigger a panelsopen event + thisView.$el.find('.panel-dialog').trigger('panelsopen'); + + // If the main dialog is closed from this point on, save the widget content + thisView.on('close_dialog', thisView.saveWidget, thisView); + }, + 'html' + ); + }, + + /** + * Save the widget from the form to the model + */ + saveWidget: function(){ + // Get the values from the form and assign the new values to the model + var values = this.getFormValues(); + if(typeof values.widgets == 'undefined') return; + values = values.widgets; + values = values[Object.keys(values)[0]]; + + this.model.setValues(values); + this.model.set('raw', true); // We've saved from the widget form, so this is now raw + + if( this.styles.stylesLoaded ) { + // If the styles view has loaded + var style = {}; + try { + var style = this.getFormValues('.so-sidebar .so-visual-styles').style; + } + catch (e) { + } + this.model.set('style', style); + } + }, + + saveHistory: function(){ + this.builder.addHistoryEntry('widget_edited'); + this.closeDialog(); + }, + + deleteHandler: function(){ + + if(this.builder.liveEditor.displayed) { + // We need to instantly destroy the widget + this.model.destroy(); + this.builder.liveEditor.refreshWidgets(); + } + else { + this.model.trigger('visual_destroy'); + } + + this.closeDialog(); + + return false; + }, + + duplicateHandler: function(){ + this.model.trigger('user_duplicate'); + + if(this.builder.liveEditor.displayed) { + this.builder.liveEditor.refreshWidgets(); + } + + this.closeDialog(); + + return false; + } + + } ); + + /** + * The dialog box for displaying prebuilt layouts. + */ + panels.dialog.prebuilt = panels.view.dialog.extend( { + + entryTemplate : _.template( $('#siteorigin-panels-dialog-prebuilt-entry').html() ), + builder: null, + dialogClass : 'so-panels-dialog-prebuilt-layouts', + + layoutCache : {}, + currentTab : false, + + events: { + 'click .so-close': 'closeDialog', + 'click .so-sidebar-tabs li a' : 'tabClickHandler', + 'click .so-content .layout' : 'layoutClickHandler', + 'keyup .so-sidebar-search' : 'searchHandler' + }, + + /** + * Initialize the prebuilt dialog. + */ + initializeDialog: function(){ + var thisView = this; + + this.on('open_dialog', function(){ + thisView.$('.so-sidebar-tabs li a[href="#prebuilt"]').click(); + }); + }, + + /** + * Render the prebuilt layouts dialog + */ + render: function(){ + this.renderDialog( this.parseDialogContent( $('#siteorigin-panels-dialog-prebuilt').html(), {} ) ); + }, + + /** + * + * @param e + * @return {boolean} + */ + tabClickHandler: function(e){ + this.$('.so-sidebar-tabs li').removeClass('tab-active'); + + var $$ = $(e.target); + var tab = $$.attr('href').split('#')[1]; + $$.parent().addClass( 'tab-active' ); + + var thisView = this; + + // Empty everything + this.$('.so-content').empty(); + + this.currentTab = tab; + + if( typeof this.layoutCache[tab] == 'undefined' ) { + // We need to load the tab items from the server + this.$('.so-content').addClass('so-panels-loading'); + + $.post( + ajaxurl, + { + action: 'so_panels_prebuilt_layouts', + type: tab + }, + function(layouts){ + thisView.layoutCache[ tab ] = layouts; + thisView.$( '.so-content' ).removeClass( 'so-panels-loading' ); + thisView.displayLayouts( tab, layouts ); + } + ); + } + else { + thisView.displayLayouts(tab, this.layoutCache[tab]); + } + + return false; + }, + + /** + * Display a list of layouts taking into account the search argument + */ + displayLayouts: function(type, layouts){ + var c = this.$('.so-content').empty(); + var query = this.$('.so-sidebar-search').val().toLowerCase(); + + if( typeof layouts.error_message != 'undefined' ) { + this.$('.so-content').append( + $('
').html( layouts.error_message ) + ); + return; + } + + for(var lid in layouts) { + // Exclude the current post if we have one + if( type != 'prebuilt' && lid == $('#post_ID').val() ) continue; + if(query != '' && layouts[lid].name.toLowerCase().indexOf( query ) === -1 ) continue; + + var $l = $( this.entryTemplate( { + name: layouts[lid].name, + description: layouts[lid].description + } ) ); + + // Create and append the + $l.appendTo(c).data({'type' : type, 'lid' : lid}); + } + }, + + /** + * Make the layout selected. + * @param e + */ + layoutClickHandler: function(e){ + var layout = $(e.target).closest('.layout'); + + this.loadLayout( + layout.data('type'), + layout.data('lid') + ); + + return false; + }, + + /** + * Load the layout into the main builder + */ + loadLayout: function(type, lid){ + var thisView = this; + + if( !confirm(panelsOptions.loc.prebuilt_confirm) ) return false; + this.setStatusMessage(panelsOptions.loc.prebuilt_loading, true); + + $.post( + ajaxurl, + { + action: 'so_panels_get_prebuilt_layout', + type: type, + lid: lid + }, + function(layout){ + // TODO check for an error message + thisView.setStatusMessage('', false); + thisView.builder.addHistoryEntry('prebuilt_loaded'); + + console.log(layout); + + thisView.builder.model.loadPanelsData(layout); + thisView.closeDialog(); + + } + ); + }, + + /** + * Handle an update to the search + */ + searchHandler: function(){ + if( this.currentTab == false || typeof this.layoutCache[ this.currentTab ] == 'undefined') return; + this.displayLayouts(this.currentTab, this.layoutCache[ this.currentTab ] ); + } + + } ); + + /** + * The dialog for adding and editing a row + */ + panels.dialog.row = panels.view.dialog.extend( { + + cellPreviewTemplate : _.template( $('#siteorigin-panels-dialog-row-cell-preview').html() ), + + events: { + 'click .so-close': 'closeDialog', + + // Toolbar buttons + 'click .so-toolbar .so-save': 'saveHandler', + 'click .so-toolbar .so-insert': 'insertHandler', + 'click .so-toolbar .so-delete': 'deleteHandler', + 'click .so-toolbar .so-duplicate': 'duplicateHandler', + + // Changing the row + 'change .row-set-form > *': 'setCellsFromForm', + 'click .row-set-form button.set-row': 'setCellsFromForm' + }, + + dialogClass : 'so-panels-dialog-row-edit', + styleType : 'row', + + /* This is used by */ + dialogType : 'edit', + + /** + * The current settings, not yet saved to the model + */ + row : { + // This is just the cell weights, cell content is not edited by this dialog + cells : [ ], + // The style settings of the row + style : { } + }, + + initializeDialog: function(){ + this.on('open_dialog', function(){ + if( typeof this.model != 'undefined' && this.model.cells.length != 0 ) { + this.setRowModel( this.model ); + } + else { + this.setRowModel( null ); + } + + this.regenerateRowPreview(); + }, this); + + // This is the default row layout + this.row = { + cells : [0.5, 0.5], + style : { } + } + }, + + /** + * + * @param dialogType Either "edit" or "create" + */ + setRowDialogType: function(dialogType){ + this.dialogType = dialogType; + }, + + /** + * Render the new row dialog + */ + render: function(dialogType){ + this.renderDialog( this.parseDialogContent( $('#siteorigin-panels-dialog-row').html(), { dialogType: this.dialogType } ) ); + + if( this.dialogType == 'edit' ) { + // Now we need to attach the style window + this.styles = new panels.view.styles(); + this.styles.model = this.model; + this.styles.render( 'row' ); + this.styles.attach( this.$('.so-sidebar.so-right-sidebar') ); + + // Handle the loading class + this.styles.on('styles_loaded', function(){ + this.$('.so-sidebar.so-right-sidebar').removeClass('so-panels-loading'); + }, this); + this.$('.so-sidebar.so-right-sidebar').addClass('so-panels-loading'); + } + + if( typeof this.model != 'undefined' ) { + // Set the initial value of the + this.$('input.so-row-field').val( this.model.cells.length ); + } + + var thisView = this; + this.$('input.so-row-field').keyup( function(){ + $(this).trigger('change'); + } ); + + return this; + }, + + /** + * Set the row model we'll be using for this dialog. + * + * @param model + */ + setRowModel: function(model){ + this.model = model; + if( this.model == null ) return; + + // Set the rows to be a copy of the model + this.row = { + cells: this.model.cells.map( function(cell){ + return cell.get('weight'); + } ), + style: { } + } + + // Set the initial value of the cell field. + this.$('input.so-row-field').val( this.model.cells.length ); + + return this; + }, + + /** + * Regenerate the row preview and resizing interface. + * + * @todo refactor this so we use the original row view. + */ + regenerateRowPreview: function(){ + var thisDialog = this; + var rowPreview = this.$('.row-preview'); + + rowPreview.empty(); + + var timeout; + + // Represent the cells + _.each(this.row.cells, function(cell, i){ + var newCell = $( this.cellPreviewTemplate( { weight: cell } ) ); + rowPreview.append( newCell ); + + var prevCell = newCell.prev(); + var handle; + + if( prevCell.length != 0 ) { + handle = $('
'); + handle + .appendTo( newCell ) + .dblclick(function(){ + var t = thisDialog.row.cells[i] + thisDialog.row.cells[i-1]; + thisDialog.row.cells[i] = thisDialog.row.cells[i-1] = t/2; + thisDialog.scaleRowWidths(); + }); + + handle.draggable({ + axis: 'x', + containment: rowPreview, + start: function(e, ui){ + + // Create the clone for the current cell + var newCellClone = newCell.clone().appendTo(ui.helper).css({ + position : 'absolute', + top : '0', + width : newCell.outerWidth(), + left : 6, + height: newCell.outerHeight() + }); + newCellClone.find('.resize-handle').remove(); + + // Create the clone for the previous cell + var prevCellClone = prevCell.clone().appendTo(ui.helper).css({ + position : 'absolute', + top : '0', + width : prevCell.outerWidth(), + right : 6, + height: prevCell.outerHeight() + }); + prevCellClone.find('.resize-handle').remove(); + + $(this).data({ + 'newCellClone' : newCellClone, + 'prevCellClone' : prevCellClone + }); + + // Hide the + newCell.find('> .preview-cell-in').css('visibility', 'hidden'); + prevCell.find('> .preview-cell-in').css('visibility', 'hidden'); + }, + drag: function(e, ui){ + // Calculate the new cell and previous cell widths as a percent + var ncw = thisDialog.row.cells[i] - ( ( ui.position.left + 6 ) / rowPreview.width() ); + var pcw = thisDialog.row.cells[i-1] + ( ( ui.position.left + 6 ) / rowPreview.width() ); + + var helperLeft = ui.helper.offset().left - rowPreview.offset().left - 6; + + $(this).data('newCellClone').css('width', rowPreview.width() * ncw ) + .find('.preview-cell-weight').html( Math.round(ncw*1000)/10 ); + + $(this).data('prevCellClone').css('width', rowPreview.width() * pcw ) + .find('.preview-cell-weight').html( Math.round(pcw*1000)/10 ); + }, + stop: function(e, ui){ + // Remove the clones + $(this).data('newCellClone').remove(); + $(this).data('prevCellClone').remove(); + + // Reshow the main cells + newCell.find('.preview-cell-in').css('visibility', 'visible'); + prevCell.find('.preview-cell-in').css('visibility', 'visible'); + + // Calculate the new cell weights + var offset = ui.position.left + 6; + var percent = offset / rowPreview.width(); + + // Ignore this if any of the cells are below 2% in width. + if( thisDialog.row.cells[i] - percent > 0.02 && thisDialog.row.cells[i-1] + percent > 0.02 ) { + thisDialog.row.cells[i] -= percent; + thisDialog.row.cells[i-1] += percent; + } + + thisDialog.scaleRowWidths(); + ui.helper.css('left', -6); + } + }); + } + + // Make this row weight click editable + newCell.find('.preview-cell-weight').click(function(ci){ + + // Disable the draggable while entering values + thisDialog.$('.resize-handle').css('pointer-event', 'none').draggable('disable'); + + rowPreview.find('.preview-cell-weight').each( function(){ + var $$ = $(this).hide(); + $('') + .val( parseFloat( $$.html() ) ).insertAfter( $$ ) + .focus( function(){ + clearTimeout( timeout ); + } ) + .keyup(function(e){ + // Enter is clicked + if(e.keyCode == 13){ + e.preventDefault(); + + $(this).removeClass('no-user-interacted'); + + // Select the next input + var inputs = rowPreview.find('.preview-cell-weight-input'); + var index = inputs.index( $(this) ); + + if(index == inputs.length - 1) index = 0; // Go to first input + else index = index + 1; // Go to next + + var next = rowPreview.find('.preview-cell-weight-input').eq( index ); + + // Either go to the next input or blur to set + if( !next.hasClass('no-user-interacted') ) $(this).blur(); + else next.select(); + } + }) + .blur( function(){ + timeout = setTimeout( function(){ + // If there are no weight inputs, then skip this + if( rowPreview.find( '.preview-cell-weight-input').length == 0 ) return; + + // Go through all the inputs + var rowWeights = []; + rowPreview.find( '.preview-cell-weight-input' ).each(function(i, el){ + var val = parseFloat( $(el).val() ); + if( val == NaN ) val = 1 / thisDialog.cells.length; + else val = val / 100; + + rowWeights.push( val ); + }); + + // Make sure the sum is 1 + var sum = 0; + for( var j = 0; j < rowWeights.length; j++ ) { + sum += rowWeights[j]; + } + for( var j = 0; j < rowWeights.length; j++ ) { + rowWeights[j] = rowWeights[j] / sum; + } + + // Set the new cell weights and regenerate the preview. + if( Math.min.apply(Math, rowWeights) > 0.01 ) { + thisDialog.row.cells = rowWeights; + } + + thisDialog.regenerateRowPreview(); + + }, 100 ); + } ) + .click( function(){ + rowPreview.find('.preview-cell-weight-input').addClass('no-user-interacted'); + $(this).select(); + } ); + } ); + + $(this).siblings('.preview-cell-weight-input').select(); + + }); + + }, this); + }, + + /** + * Visually scale the row widths based on the cell weights + */ + scaleRowWidths: function(){ + var thisDialog = this; + this.$('.row-preview .preview-cell').each(function(i, el){ + $(el) + .css('width', thisDialog.row.cells[i] * 100 + "%") + .find('.preview-cell-weight').html( Math.round( thisDialog.row.cells[i] * 1000 )/10 ) + }); + }, + + /** + * Get the weights from the + */ + setCellsFromForm: function(){ + var f = { + 'cells' : parseInt( this.$el.find('.row-set-form input[name="cells"]').val() ), + 'ratio' : parseFloat( this.$el.find('.row-set-form select[name="ratio"]').val() ), + 'direction' : this.$el.find('.row-set-form select[name="ratio_direction"]').val() + } + var cells = []; + + // Ignore this if the ratio or cell count is NaN + if( isNaN(f.cells) || isNaN(f.ratio) ) return; + + if( f.cells < 1 ) { + this.$el.find('.row-set-form input[name="cells"]').val(1); + f.cells = 1; + } + else if (f.cells > 20) { + this.$el.find('.row-set-form input[name="cells"]').val(20); + f.cells = 20; + } + + // Now, lets create some cells + var currentWeight = 1; + for( var i = 0; i < f.cells; i++ ) { + cells.push (currentWeight); + currentWeight *= f.ratio; + } + + // Now lets make sure that the row weights add up to 1 + + var totalRowWeight = _.reduce( cells, function(memo, weight){ return memo + weight }); + cells = _.map(cells, function(cell){ + return cell/totalRowWeight; + }); + + // Don't return cells that are too small + cells = _.filter(cells, function(cell){ return cell > 0.01 }); + + if(f.direction == 'left') { + cells = cells.reverse(); + } + + this.row.cells = cells; + this.regenerateRowPreview(); + + // Remove the button primary class + this.$el.find('.row-set-form .so-button-row-set').removeClass('button-primary'); + }, + + /** + * Handle a click on the dialog left bar tab + */ + tabClickHandler : function($t){ + if($t.attr('href') == '#row-layout') { + this.$('.so-panels-dialog').addClass('so-panels-dialog-has-right-sidebar'); + } + else { + this.$('.so-panels-dialog').removeClass('so-panels-dialog-has-right-sidebar'); + } + }, + + /** + * Update the current model with what we have in the dialog + */ + updateModel: function(){ + // Set the cells + this.model.setCells( this.row.cells ); + + // Update the styles if they've loaded + if ( typeof this.styles != 'undefined' && this.styles.stylesLoaded ) { + // This is an edit dialog, so there are styles + var style = {}; + try { + var style = this.getFormValues('.so-sidebar .so-visual-styles').style; + } + catch( e ) { } + + this.model.set('style', style); + } + }, + + /** + * Insert the new row + */ + insertHandler: function(){ + this.builder.addHistoryEntry('row_added'); + + this.model = new panels.model.row(); + this.updateModel(); + + // Set up the model and add it to the builder + this.model.collection = this.builder.model.rows; + this.builder.model.rows.add( this.model ); + + this.closeDialog(); + + return false; + }, + + /** + * We'll just save this model and close the dialog + */ + saveHandler: function(){ + this.builder.addHistoryEntry('row_edited'); + this.updateModel(); + this.closeDialog(); + + return false; + }, + + /** + * The user clicks delete, so trigger deletion on the row model + */ + deleteHandler: function(){ + // Trigger a destroy on the model that will happen with a visual indication to the user + this.model.trigger('visual_destroy'); + this.closeDialog(); + + return false; + }, + + /** + * Duplicate this row + */ + duplicateHandler: function(){ + this.builder.addHistoryEntry('row_duplicated'); + + var duplicateRow = this.model.clone( this.builder.model ); + + this.builder.model.rows.add( duplicateRow, { + at: this.builder.model.rows.indexOf( this.model ) + 1 + } ); + + this.closeDialog(); + + return false; + } + + } ); + + // Return the SiteOrigin Panels app + window.siteoriginPanels = panels; + +} )( jQuery, _, soPanelsOptions ); + +// Set up Page Builder if we're on the main interface +jQuery( function($){ + + var container = false, field = false, form = false, postId = false; + + if( $('#siteorigin-panels-metabox').length && $('form#post').length ) { + container = $( '#siteorigin-panels-metabox' ); + field = $( '#siteorigin-panels-metabox .siteorigin-panels-data-field' ); + form = $('form#post'); + postId = $('#post_ID').val(); + } + else if( $('div#panels-home-page.wrap').length ) { + // We're dealing with the custom home page interface + var $$ = $('div#panels-home-page.wrap'); + container = $$.find('.siteorigin-panels-builder'); + field = $$.find('input[name="panels_data"]'); + form = $$.find('form'); + postId = $('#panels-home-page').data('post-id'); + } + + if( container != false ) { + // If we have a container, then set up the main builder + var panels = window.siteoriginPanels; + + // Create the main builder model + var builderModel = new panels.model.builder(); + + // Now for the view to display the builder + var builderView = new panels.view.builder( { + model: builderModel + } ); + + // Set up the builder view + builderView + .render() + .attach( { container: container } ) + .setDataField( field ) + .attachToEditor() + .addLiveEditor( postId ) + .addHistoryBrowser(); + + // When the form is submitted, update the panels data + form.submit( function(e){ + // Refresh the data + builderModel.refreshPanelsData(); + } ); + + container.removeClass('so-panels-loading'); + } +} ); + +// A basic jQuery plugin for setting up a Page Builder widget. +(function ( $ ) { + + var panels = window.siteoriginPanels; + + $.fn.soPanelsSetupBuilderWidget = function () { + + return this.each(function(){ + var $$ = $(this); + var widgetId = $$.closest('form').find('.widget-id').val(); + + // Exit if this isn't a real widget + if( widgetId != null && widgetId.indexOf('__i__') > -1 ) { + return; + } + + // Create the main builder model + var builderModel = new panels.model.builder(); + + // Now for the view to display the builder + var builderView = new panels.view.builder( { + model: builderModel + } ); + + // Save panels data when we close the dialog, if we're in a dialog + var dialog = $$.closest('.so-panels-dialog-wrapper').data('view'); + if( dialog != null ) { + dialog.on('close_dialog', function(){ + builderModel.refreshPanelsData(); + } ); + + dialog.on('open_dialog_complete', function(){ + // Make sure the new layout widget is always properly setup + builderView.trigger('builder_resize'); + }); + + dialog.model.on('destroy', function(){ + // Destroy the builder + builderModel.emptyRows().destroy(); + } ); + + // Set the parent for all the sub dialogs + builderView.setDialogParents(soPanelsOptions.loc.layout_widget, dialog); + } + + // Basic setup for the builder + var isWidget = Boolean( $$.closest('.widget-content').length ); + builderView + .render() + .attach( { container: $$, dialog: isWidget } ) + .setDataField( $$.find('input.panels-data') ); + + if( isWidget ) { + // Set up the dialog opening + builderView.setDialogParents(soPanelsOptions.loc.layout_widget, builderView.dialog); + $$.find( '.siteorigin-panels-display-builder').click(function(){ + builderView.dialog.openDialog(); + }); + } + else { + // Remove the dialog opener button, this is already being displayed in a page builder dialog. + $$.find( '.siteorigin-panels-display-builder').parent().remove(); + } + + }); + }; + + // Setup new widgets when they're added in the standard widget interface + $(document).on('widget-added', function(e, widget) { + $(widget).find('.siteorigin-page-builder-widget').soPanelsSetupBuilderWidget(); + }); + + // Setup existing widgets on the page (for the widgets interface) + $(function(){ + $('.siteorigin-page-builder-widget').soPanelsSetupBuilderWidget(); + }); + +})(jQuery); \ No newline at end of file diff --git a/js/styling.js b/js/styling.js new file mode 100644 index 000000000..a06370b98 --- /dev/null +++ b/js/styling.js @@ -0,0 +1,45 @@ +jQuery(function($){ + + // This will handle stretching the cells. + $('.siteorigin-panels-stretch.panel-row-style').each(function(){ + var $$ = $(this); + + var onResize = function(){ + + $$.css({ + 'margin-left' : 0, + 'margin-right' : 0, + 'padding-left' : 0, + 'padding-right' : 0 + }); + + var leftSpace = $$.offset().left; + var rightSpace = $(window).outerWidth() - $$.offset().left - $$.parent().outerWidth(); + + $$.css({ + 'margin-left' : -leftSpace, + 'margin-right' : -rightSpace, + 'padding-left' : $$.data('stretch-type') == 'full' ? leftSpace : 0, + 'padding-right' : $$.data('stretch-type') == 'full' ? rightSpace : 0 + }); + + var cells = $$.find('> .panel-grid-cell'); + + if( $$.data('stretch-type') == 'full-stretched' && cells.length == 1 ) { + cells.css({ + 'padding-left' : 0, + 'padding-right' : 0 + }); + } + } + + $(window).resize( onResize ); + onResize(); + + $$.css({ + 'border-left' : 0, + 'border-right' : 0 + }); + }); + +}); \ No newline at end of file diff --git a/lang/siteorigin-panels.po b/lang/siteorigin-panels.po new file mode 100644 index 000000000..d6684e42a --- /dev/null +++ b/lang/siteorigin-panels.po @@ -0,0 +1,827 @@ +msgid "" +msgstr "" +"Project-Id-Version: Page Builder by SiteOrigin\n" +"POT-Creation-Date: 2014-09-15 22:17+0200\n" +"PO-Revision-Date: 2014-09-15 22:17+0200\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 1.6.7\n" +"X-Poedit-Basepath: ..\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;" +"esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;" +"_nx_noop:3c,1,2;__ngettext_noop:1,2\n" +"X-Poedit-SearchPath-0: .\n" + +#: inc/legacy.php:22 siteorigin-panels.php:104 +msgid "Home" +msgstr "" + +#: inc/notice.php:17 +#, php-format +msgid "" +"You've successfully installed Page Builder version %s. " +msgstr "" + +#: inc/notice.php:20 +#, php-format +msgid "" +"You've successfully updated Page Builder to version %s. " +msgstr "" + +#: inc/notice.php:24 +#, php-format +msgid "" +"Please post on our support forums if " +"you have any issues and sign up to our " +"newsletter to stay up to date." +msgstr "" + +#: inc/notice.php:31 inc/notice.php:102 +msgid "Support Forums" +msgstr "" + +#: inc/notice.php:32 siteorigin-panels.php:1170 +msgid "Newsletter" +msgstr "" + +#: inc/notice.php:34 inc/notice.php:103 +msgid "Dismiss" +msgstr "" + +#: inc/notice.php:84 +msgid "" +"One or more of your active plugins are known to be incompatible with Page " +"Builder." +msgstr "" + +#: inc/notice.php:93 +msgid "More" +msgstr "" + +#: inc/options.php:68 tpl/options.php:5 +msgid "SiteOrigin Page Builder" +msgstr "" + +#: inc/options.php:107 +msgid "Post types that will have the page builder available" +msgstr "" + +#: inc/options.php:122 +msgid "Enabled" +msgstr "" + +#: inc/options.php:127 +msgid "px" +msgstr "" + +#: inc/revisions.php:50 +msgid "Page Builder Content" +msgstr "" + +#: inc/styles.php:19 +msgid "Class" +msgstr "" + +#: inc/styles.php:22 widgets/basic.php:59 widgets/basic.php:482 +#: widgets/widgets.php:625 +msgid "Default" +msgstr "" + +#: inc/styles.php:36 +msgid "Your theme doesn't provide any visual style fields. " +msgstr "" + +#: siteorigin-panels.php:65 +msgid "Custom Home Page Builder" +msgstr "" + +#: siteorigin-panels.php:66 +msgid "Home Page" +msgstr "" + +#: siteorigin-panels.php:78 siteorigin-panels.php:87 siteorigin-panels.php:336 +msgid "Page Builder" +msgstr "" + +#: siteorigin-panels.php:229 +msgid "Insert" +msgstr "" + +#: siteorigin-panels.php:230 +msgid "cancel" +msgstr "" + +#: siteorigin-panels.php:231 +msgid "Delete" +msgstr "" + +#: siteorigin-panels.php:232 +msgid "Duplicate" +msgstr "" + +#: siteorigin-panels.php:233 +msgid "Edit" +msgstr "" + +#: siteorigin-panels.php:234 +msgid "Done" +msgstr "" + +#: siteorigin-panels.php:235 +msgid "Undo" +msgstr "" + +#: siteorigin-panels.php:236 +msgid "Add" +msgstr "" + +#: siteorigin-panels.php:239 +msgid "Columns deleted" +msgstr "" + +#: siteorigin-panels.php:240 +msgid "Widget deleted" +msgstr "" + +#: siteorigin-panels.php:241 +msgid "" +"Are you sure you want to load this layout? It will overwrite your current " +"page." +msgstr "" + +#: siteorigin-panels.php:242 +#, php-format +msgid "Edit %s Widget" +msgstr "" + +#: siteorigin-panels.php:262 +msgid "Install the missing widget" +msgstr "" + +#: siteorigin-panels.php:853 +msgid "Edit Home Page" +msgstr "" + +#: siteorigin-panels.php:965 +msgid "Untitled" +msgstr "" + +#: siteorigin-panels.php:966 +msgid "Unpublished" +msgstr "" + +#: siteorigin-panels.php:971 +#, php-format +msgid "Clone Page: %s" +msgstr "" + +#: siteorigin-panels.php:984 +msgid "Clone: Current Home Page" +msgstr "" + +#: siteorigin-panels.php:1003 +msgid "Recommended Plugins and Widgets" +msgstr "" + +#: siteorigin-panels.php:1004 +msgid "Free plugins that work well with Page Builder" +msgstr "" + +#: siteorigin-panels.php:1097 +msgid "This widget is not available, please install the missing plugin." +msgstr "" + +#: siteorigin-panels.php:1169 +msgid "Support Forum" +msgstr "" + +#: tpl/admin-home-page.php:7 +msgid "Custom Home Page" +msgstr "" + +#: tpl/admin-home-page.php:10 +msgid "ON" +msgstr "" + +#: tpl/admin-home-page.php:11 +msgid "OFF" +msgstr "" + +#: tpl/admin-home-page.php:24 +#, php-format +msgid "Home page updated. View page" +msgstr "" + +#: tpl/admin-home-page.php:31 +msgid "Preview Changes" +msgstr "" + +#: tpl/admin-home-page.php:36 +msgid "Save Home Page" +msgstr "" + +#: tpl/admin-home-page.php:44 +msgid "This interface requires Javascript" +msgstr "" + +#: tpl/help.php:2 +msgid "" +"You can use SiteOrigin Page Builder to create home and sub pages, filled " +"your own widgets." +msgstr "" + +#: tpl/help.php:3 +msgid "The page layouts are responsive and fully customizable." +msgstr "" + +#: tpl/help.php:8 +#, php-format +msgid "" +"Read the full documentation on SiteOrigin." +msgstr "" + +#: tpl/help.php:9 +#, php-format +msgid "" +"Ask a question on our support forum if you " +"need help and sign up to our newsletter to " +"stay up to date with future developments." +msgstr "" + +#: tpl/metabox-panels.php:14 +msgid "Add Widget" +msgstr "" + +#: tpl/metabox-panels.php:15 tpl/metabox-panels.php:59 +msgid "Add Row" +msgstr "" + +#: tpl/metabox-panels.php:17 +msgid "Prebuilt Layouts" +msgstr "" + +#: tpl/metabox-panels.php:20 +msgid "Switch to Editor" +msgstr "" + +#: tpl/metabox-panels.php:27 +msgid "Add New Widget" +msgstr "" + +#: tpl/metabox-panels.php:60 widgets/basic.php:76 +msgid "Columns" +msgstr "" + +#: tpl/metabox-panels.php:67 +msgid "Insert Prebuilt Page Layout" +msgstr "" + +#: tpl/metabox-panels.php:68 +msgid "Page Layout" +msgstr "" + +#: tpl/metabox-panels.php:70 +msgid "Select Layout" +msgstr "" + +#: tpl/metabox-panels.php:74 +msgid "Untitled Layout" +msgstr "" + +#: tpl/metabox-panels.php:83 +msgid "Row Visual Style" +msgstr "" + +#: tpl/options.php:11 +msgid "General" +msgstr "" + +#: tpl/options.php:15 +msgid "Post Types" +msgstr "" + +#: tpl/options.php:25 +msgid "Copy Content" +msgstr "" + +#: tpl/options.php:26 +msgid "Copy content from Page Builder into the standard content editor." +msgstr "" + +#: tpl/options.php:32 +msgid "Animations" +msgstr "" + +#: tpl/options.php:33 +msgid "Disable animations for improved performance." +msgstr "" + +#: tpl/options.php:39 +msgid "Bundled Widgets" +msgstr "" + +#: tpl/options.php:40 +msgid "Include the bundled widgets." +msgstr "" + +#: tpl/options.php:48 +msgid "Display" +msgstr "" + +#: tpl/options.php:57 +msgid "Responsive Layout" +msgstr "" + +#: tpl/options.php:58 +msgid "Should the layout collapse for mobile devices." +msgstr "" + +#: tpl/options.php:64 +msgid "Mobile Width" +msgstr "" + +#: tpl/options.php:70 +msgid "Row Bottom Margin" +msgstr "" + +#: tpl/options.php:76 +msgid "Cell Side Margins" +msgstr "" + +#: tpl/options.php:87 +msgid "Save Settings" +msgstr "" + +#: video/jplayer/skins/premium/gui.php:3 +#: video/jplayer/skins/siteorigin/gui.php:3 +msgid "play" +msgstr "" + +#: video/jplayer/skins/premium/gui.php:4 +#: video/jplayer/skins/siteorigin/gui.php:4 +msgid "pause" +msgstr "" + +#: video/jplayer/skins/premium/gui.php:5 +#: video/jplayer/skins/siteorigin/gui.php:5 +msgid "stop" +msgstr "" + +#: video/jplayer/skins/premium/gui.php:7 +#: video/jplayer/skins/siteorigin/gui.php:7 +msgid "full screen" +msgstr "" + +#: video/jplayer/skins/premium/gui.php:8 +#: video/jplayer/skins/siteorigin/gui.php:8 +msgid "restore screen" +msgstr "" + +#: video/jplayer/skins/premium/gui.php:10 +msgid "mute" +msgstr "" + +#: video/jplayer/skins/premium/gui.php:11 +msgid "unmute" +msgstr "" + +#: widgets/basic.php:7 +msgid "Gallery (PB)" +msgstr "" + +#: widgets/basic.php:9 +msgid "Displays a gallery." +msgstr "" + +#: widgets/basic.php:48 +msgid "Gallery Images" +msgstr "" + +#: widgets/basic.php:49 +msgid "edit gallery" +msgstr "" + +#: widgets/basic.php:53 +msgid "" +"Comma separated attachment IDs. Defaults to all current page's attachments." +msgstr "" + +#: widgets/basic.php:57 +msgid "Image Size" +msgstr "" + +#: widgets/basic.php:60 +msgid "Large" +msgstr "" + +#: widgets/basic.php:61 +msgid "Medium" +msgstr "" + +#: widgets/basic.php:62 +msgid "Thumbnail" +msgstr "" + +#: widgets/basic.php:63 +msgid "Full" +msgstr "" + +#: widgets/basic.php:71 +msgid "Gallery Type" +msgstr "" + +#: widgets/basic.php:81 +msgid "Link To" +msgstr "" + +#: widgets/basic.php:83 +msgid "Attachment Page" +msgstr "" + +#: widgets/basic.php:84 +msgid "File" +msgstr "" + +#: widgets/basic.php:85 widgets/basic.php:145 widgets/basic.php:455 +#: widgets/widgets.php:598 +msgid "None" +msgstr "" + +#: widgets/basic.php:97 +msgid "Post Content (PB)" +msgstr "" + +#: widgets/basic.php:99 +msgid "Displays some form of post content form the current post." +msgstr "" + +#: widgets/basic.php:146 widgets/basic.php:420 +#: widgets/widgets/call-to-action/call-to-action.php:15 +#: widgets/widgets/list/list.php:15 widgets/widgets/price-box/price-box.php:15 +msgid "Title" +msgstr "" + +#: widgets/basic.php:147 +msgid "Featured Image" +msgstr "" + +#: widgets/basic.php:152 +msgid "Display Content" +msgstr "" + +#: widgets/basic.php:167 +msgid "Image (PB)" +msgstr "" + +#: widgets/basic.php:169 +msgid "Displays a simple image." +msgstr "" + +#: widgets/basic.php:202 widgets/widgets/animated-image/animated-image.php:15 +msgid "Image URL" +msgstr "" + +#: widgets/basic.php:206 widgets/widgets/button/button.php:19 +msgid "Destination URL" +msgstr "" + +#: widgets/basic.php:222 +msgid "Post Loop (PB)" +msgstr "" + +#: widgets/basic.php:224 +msgid "Displays a post loop." +msgstr "" + +#: widgets/basic.php:409 +msgid "Your theme doesn't have any post loops." +msgstr "" + +#: widgets/basic.php:424 +msgid "Template" +msgstr "" + +#: widgets/basic.php:439 widgets/widgets.php:584 +msgid "Post Type" +msgstr "" + +#: widgets/basic.php:448 widgets/widgets.php:591 +msgid "Posts Per Page" +msgstr "" + +#: widgets/basic.php:453 widgets/widgets.php:596 +msgid "Order By" +msgstr "" + +#: widgets/basic.php:456 widgets/widgets.php:599 +msgid "Post ID" +msgstr "" + +#: widgets/basic.php:457 widgets/widgets.php:600 +msgid "Author" +msgstr "" + +#: widgets/basic.php:458 widgets/basic.php:459 widgets/widgets.php:601 +#: widgets/widgets.php:602 widgets/widgets/testimonial/testimonial.php:15 +msgid "Name" +msgstr "" + +#: widgets/basic.php:460 widgets/widgets.php:603 +msgid "Date" +msgstr "" + +#: widgets/basic.php:461 widgets/widgets.php:604 +msgid "Modified" +msgstr "" + +#: widgets/basic.php:462 widgets/widgets.php:605 +msgid "Parent" +msgstr "" + +#: widgets/basic.php:463 widgets/widgets.php:606 +msgid "Random" +msgstr "" + +#: widgets/basic.php:464 widgets/widgets.php:607 +msgid "Comment Count" +msgstr "" + +#: widgets/basic.php:465 widgets/basic.php:466 widgets/widgets.php:608 +msgid "Menu Order" +msgstr "" + +#: widgets/basic.php:467 +msgid "Post In Order" +msgstr "" + +#: widgets/basic.php:472 widgets/widgets.php:614 +msgid "Order" +msgstr "" + +#: widgets/basic.php:474 widgets/widgets.php:617 +msgid "Descending" +msgstr "" + +#: widgets/basic.php:475 widgets/widgets.php:616 +msgid "Ascending" +msgstr "" + +#: widgets/basic.php:480 widgets/widgets.php:623 +msgid "Sticky Posts" +msgstr "" + +#: widgets/basic.php:483 widgets/widgets.php:626 +msgid "Ignore Sticky" +msgstr "" + +#: widgets/basic.php:484 widgets/widgets.php:627 +msgid "Exclude Sticky" +msgstr "" + +#: widgets/basic.php:485 widgets/widgets.php:628 +msgid "Only Sticky" +msgstr "" + +#: widgets/basic.php:490 +msgid "More Link " +msgstr "" + +#: widgets/basic.php:492 +msgid "If the template supports it, cut posts and display the more link." +msgstr "" + +#: widgets/basic.php:496 +msgid "Additional " +msgstr "" + +#: widgets/basic.php:498 widgets/widgets.php:635 +#, php-format +msgid "" +"Additional query arguments. See query_posts." +msgstr "" + +#: widgets/basic.php:512 +msgid "Embedded Video (PB)" +msgstr "" + +#: widgets/basic.php:514 +msgid "Embeds a video." +msgstr "" + +#: widgets/basic.php:552 +msgid "Video" +msgstr "" + +#: widgets/basic.php:568 +msgid "Self Hosted Video (PB)" +msgstr "" + +#: widgets/basic.php:570 +msgid "A self hosted video player." +msgstr "" + +#: widgets/basic.php:645 +msgid "Video URL" +msgstr "" + +#: widgets/basic.php:649 +msgid "Poster URL" +msgstr "" + +#: widgets/basic.php:651 +msgid "An image that displays before the video starts playing." +msgstr "" + +#: widgets/basic.php:654 +msgid "Skin" +msgstr "" + +#: widgets/basic.php:656 +msgid "SiteOrigin" +msgstr "" + +#: widgets/basic.php:657 +msgid "Premium Pixels" +msgstr "" + +#: widgets/basic.php:661 +msgid "Aspect Ratio" +msgstr "" + +#: widgets/basic.php:663 +msgid "1.777 is HD standard." +msgstr "" + +#: widgets/basic.php:668 +msgid "Auto Play Video" +msgstr "" + +#: widgets/widgets.php:204 +msgid "Style" +msgstr "" + +#: widgets/widgets.php:228 +#, php-format +msgid "%s Style" +msgstr "" + +#: widgets/widgets.php:634 +msgid "Additional Arguments" +msgstr "" + +#: widgets/widgets/animated-image/animated-image.php:6 +msgid "Animated Image (PB)" +msgstr "" + +#: widgets/widgets/animated-image/animated-image.php:8 +msgid "An image that animates in when it enters the screen." +msgstr "" + +#: widgets/widgets/animated-image/animated-image.php:19 +msgid "Animation" +msgstr "" + +#: widgets/widgets/animated-image/animated-image.php:21 +msgid "Fade In" +msgstr "" + +#: widgets/widgets/animated-image/animated-image.php:22 +msgid "Slide Up" +msgstr "" + +#: widgets/widgets/animated-image/animated-image.php:23 +msgid "Slide Down" +msgstr "" + +#: widgets/widgets/animated-image/animated-image.php:24 +msgid "Slide Left" +msgstr "" + +#: widgets/widgets/animated-image/animated-image.php:25 +msgid "Slide Right" +msgstr "" + +#: widgets/widgets/button/button.php:6 +msgid "Button (PB)" +msgstr "" + +#: widgets/widgets/button/button.php:8 +msgid "A simple button" +msgstr "" + +#: widgets/widgets/button/button.php:15 widgets/widgets/list/list.php:19 +#: widgets/widgets/testimonial/testimonial.php:27 +msgid "Text" +msgstr "" + +#: widgets/widgets/button/button.php:23 +#: widgets/widgets/call-to-action/call-to-action.php:31 +#: widgets/widgets/price-box/price-box.php:44 +#: widgets/widgets/testimonial/testimonial.php:35 +msgid "Open In New Window" +msgstr "" + +#: widgets/widgets/button/button.php:27 +msgid "Button Alignment" +msgstr "" + +#: widgets/widgets/button/button.php:29 +msgid "Left" +msgstr "" + +#: widgets/widgets/button/button.php:30 +msgid "Right" +msgstr "" + +#: widgets/widgets/button/button.php:31 +msgid "Center" +msgstr "" + +#: widgets/widgets/button/button.php:32 +msgid "Justify" +msgstr "" + +#: widgets/widgets/call-to-action/call-to-action.php:6 +msgid "Call To Action (PB)" +msgstr "" + +#: widgets/widgets/call-to-action/call-to-action.php:8 +msgid "A Call to Action block" +msgstr "" + +#: widgets/widgets/call-to-action/call-to-action.php:19 +msgid "Sub Title" +msgstr "" + +#: widgets/widgets/call-to-action/call-to-action.php:23 +#: widgets/widgets/price-box/price-box.php:36 +msgid "Button Text" +msgstr "" + +#: widgets/widgets/call-to-action/call-to-action.php:27 +#: widgets/widgets/price-box/price-box.php:40 +msgid "Button URL" +msgstr "" + +#: widgets/widgets/call-to-action/call-to-action.php:37 +#: widgets/widgets/price-box/price-box.php:49 +msgid "Button" +msgstr "" + +#: widgets/widgets/list/list.php:6 +msgid "List (PB)" +msgstr "" + +#: widgets/widgets/list/list.php:8 widgets/widgets/price-box/price-box.php:8 +#: widgets/widgets/testimonial/testimonial.php:8 +msgid "Displays a bullet list of elements" +msgstr "" + +#: widgets/widgets/list/list.php:20 widgets/widgets/price-box/price-box.php:32 +msgid "Start each new point with an asterisk (*)" +msgstr "" + +#: widgets/widgets/price-box/price-box.php:6 +msgid "Price Box (PB)" +msgstr "" + +#: widgets/widgets/price-box/price-box.php:19 +msgid "Price" +msgstr "" + +#: widgets/widgets/price-box/price-box.php:23 +msgid "Per" +msgstr "" + +#: widgets/widgets/price-box/price-box.php:27 +msgid "Information Text" +msgstr "" + +#: widgets/widgets/price-box/price-box.php:31 +msgid "Features Text" +msgstr "" + +#: widgets/widgets/price-box/price-box.php:50 +msgid "Feature List" +msgstr "" + +#: widgets/widgets/testimonial/testimonial.php:6 +msgid "Testimonial (PB)" +msgstr "" + +#: widgets/widgets/testimonial/testimonial.php:19 +msgid "Location" +msgstr "" + +#: widgets/widgets/testimonial/testimonial.php:23 +msgid "Image" +msgstr "" + +#: widgets/widgets/testimonial/testimonial.php:31 +msgid "URL" +msgstr "" diff --git a/license.txt b/license.txt new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/license.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/readme.txt b/readme.txt new file mode 100644 index 000000000..d685c21af --- /dev/null +++ b/readme.txt @@ -0,0 +1,193 @@ +=== Page Builder by SiteOrigin === +Contributors: gpriday +Tags: page builder, responsive, widget, widgets, builder, page, admin, gallery, content, cms, pages, post, css, layout, grid +Requires at least: 3.7 +Tested up to: 4.0 +Stable tag: trunk +License: GPLv3 +License URI: http://www.gnu.org/licenses/gpl.html +Donate link: http://siteorigin.com/page-builder/#donate + +Build responsive page layouts using the widgets you know and love using this simple drag and drop page builder. + +== Description == + +[vimeo http://vimeo.com/59561067] + +WordPress has evolved into a fully functional CMS. Page Builder (previously called Panels) completes the transition by giving you a way to create responsive column layouts using the widgets you know and love. + += Use Your Widgets = + +You know widgets. They're the things you add to your sidebars. Page Builder makes all your widgets even more useful by turning them into the building blocks of your pages. + +We've included a few useful widgets, but it works with a lot of other widgets and plugins out there. + += Works with Most Themes = + +Page Builder works with most well made themes. The only requirement is that your theme supports pages. And if your theme is responsive, change a few settings and boom, your layouts will work with your theme and collapse into a single column on mobile devices. + +There are loads free and premium themes that work with the Page Builder, we have our own collection of [free themes](http://siteorigin.com/) if you'd like to use one of ours. + +Page Builder [Documentation](http://siteorigin.com/page-builder/documentation/) is available on SiteOrigin and we offer free support on our [support forum](http://siteorigin.com/threads/plugin-page-builder/). If you're having strange issues, try following [this guide](http://siteorigin.com/troubleshooting/identifying-plugin-conflicts/). + += Bundled Widgets = + +To get you started, we've include a few widgets: + +* Gallery widget for inserting image galleries. +* Image widget for inserting standard images. +* Self hosted video widget for embedding your own videos. +* Post Loop to display a list of posts. This requires that your theme supports it. + +As well as some essential page elements widgets: + +* Button +* Call to Action +* List +* Price Box +* Animated Image +* Testimonial + += 3rd Party Widgets = + +Most standard widgets work with Page Builder, but here are some of our favorites. + +* [SiteOrigin Widget Bundle](http://wordpress.org/plugins/so-widgets-bundle/) for growing collection of widgets like buttons, price tables and images. +* [Black Studio TinyMCE](http://wordpress.org/plugins/black-studio-tinymce-widget/) for a visual content editing widget. +* [Meta Slider](http://wordpress.org/plugins/ml-slider/) for a responsive slider widget. + +== Installation == + +1. Upload and install Page Builder in the same way you'd install any other plugin. +2. Read the [usage documentation](http://siteorigin.com/page-builder/documentation/) on SiteOrigin. + +== Screenshots == + +1. The page builder interface. +2. Adding a new widget. This includes a live search filter to help you keep control if you have a lot of widgets. +3. Editing a widget's settings. +4. Easily undo mistakes. + +== Documentation == + +[Documentation](http://siteorigin.com/page-builder/documentation/) is available on SiteOrigin. + +== Frequently Asked Questions == + += How do I move a site created with Page Builder from one server to another? = + +We recommend the [duplicator plugin](https://wordpress.org/plugins/duplicator/). We've tested it in several instances and it always works well with Page Builder data. + += Can I bundle Page Builder with my theme? = + +Yes, provided your theme is licensed under GPL or a compatible license. If you're publishing your theme on ThemeForest, you must select the GPL license instead of their regular license. + +Page Builder is actively developed and updated, so generally I'd recommend that you have your users install the actual plugin so they can receive updates. You can try [TGM Plugin Activation](http://tgmpluginactivation.com/). + += Will plugin X work with Page Builder? = + +I've tried to ensure that Page Builder is compatible with most plugin widgets. It's best to just download Page Builder and test for yourself. + +== Changelog == + += 2.0 = +* Complete rewrite of Page Builder Javascript using Backbone. + += 1.5.4 = +* Readded inline CSS setting. +* Improved handling of missing widgets in prebuilt layouts. + += 1.5.3 = +* Fixed post loop widget issue. +* Fixed settings issue. + += 1.5.2 = +* Changed to custom settings system to fix a few settings bugs. +* Added option to display more link in post loop widget. +* Fixed SSL in widget images. + += 1.5.1 = +* Compatibility with WordPress 4.0 - needed to change how tabs function. +* Compatibility with Black Studio TinyMCE Widget 2.0. +* Namespaced Tooltip to avoid conflicts. + += 1.5 = +* Increased size of widget dialog boxes. +* Updated incompatible plugins list. +* Updated to latest version of Chosen. +* Custom Home Page feature now uses standard pages. +* Improvements to preview handling. + += 1.4.12 = +* Improved how missing widgets are handled. +* General code clean up. +* Prebuilt layouts are no longer all filtered by siteorigin_panels_data. Filtered by siteorigin_panels_prebuilt_layout when fetched. +* Added more hooks and filters. +* Incompatible plugins now includes more link to give details about incompatibility. + += 1.4.11 = +* Fixed: Issue with setting up a home page, switching themes, then not being able to disable the home page. +* Updated to be compatible with latest Black Studio TinyMCE widget. +* Added a plugin incompatibility check with an admin notice. +* Improved bundled language files. + += 1.4.10 = +* Fixed: Fixed z-indexes so that TinyMCE dropdowns (like formatting) aren't hidden. + += 1.4.9 = +* Fixed: jQuery UI dialog wasn't being enqueued properly in WordPress 3.9. + += 1.4.8 = +* Updated Post Loop widget so it now accepts post__in in additional args field. +* Added update notification. +* Added filters for before and after the row content. +* Removed references to legacy widgets. + += 1.4.7 = +* Fixed size problem in gallery widget. +* Compatibility fixes with WordPress 3.9. + += 1.4.6 = +* Widgets are now only run through their update function when modified. +* Fixed gallery widget. + += 1.4.5 = +* Fixed an issue with copy content. +* Improved handling of styles in prebuilt layouts. +* Improved error handling in Javascript. +* Fixed issue with checkboxes. + += 1.4.4 = +* Generating Page Builder content in admin is now generated with a separate request to properly handle fatal errors from widgets. +* Fixed potential issue when loading home page interface. +* Added a way for themes to specify more advanced row styles. +* Dialogs and widget forms are now only loaded when needed in order to improve performance on large pages. +* Fixed several performance bottle necks. +* Page Builder data is now saved with auto save and revisions. + += 1.4.3 = +* Improved HTML5 validation be moving styles to header and footer. +* Basic improvements to memory efficiency. +* Black Studio TinyMCE height set to 350 pixels by default. +* Fixed: Black Studio TinyMCE update error. + += 1.4.2 = +* All existing widget forms are loaded with the initial interface, rather than through AJAX. Improves performance. +* Added safety check to ensure Page Builder data loaded before into the interface before saving into the database. Helps prevent content loss. +* Small usability improvements. +* Fixed: Embedded video widget. +* Fixed: Conflict with GPP Slideshow plugin. +* Fixed: Possible z-index conflicts with other plugins that have jQuery UI CSS. +* Fixed: Constant notification about autosave being more recent than current version. + += 1.4.1 = +* Fixed: Issue that was removing content for widgets with a lot of data. +* Fixed: Issue with duplicating widgets. + += 1.4.0 = +* Changed how widget forms are loaded to improve page load times. +* Several improvements to increase compatibility with various plugins and widgets. +* Properly handle widgets with form arrays. +* CSS fixes. +* Fixed compatibility issues with Black Studio TinyMCE. +* Added more development hooks and filters. \ No newline at end of file diff --git a/siteorigin-panels.php b/siteorigin-panels.php new file mode 100644 index 000000000..f7d416a2d --- /dev/null +++ b/siteorigin-panels.php @@ -0,0 +1,1259 @@ + __( 'Home', 'siteorigin-panels' ), + 'post_status' => $_POST['siteorigin_panels_home_enabled'] == 'true' ? 'publish' : 'draft', + 'post_type' => 'page', + 'comment_status' => 'closed', + ) ); + update_option( 'siteorigin_panels_home_page_id', $page_id ); + } + else { + $page_id = get_option( 'siteorigin_panels_home_page_id' ); + } + + // Save the updated page data + $panels_data = json_decode( wp_unslash( $_POST['panels_data'] ), true); + $panels_data['widgets'] = siteorigin_panels_process_raw_widgets($panels_data['widgets']); + $panels_data = siteorigin_panels_styles_sanitize_all( $panels_data ); + + update_post_meta( $page_id, 'panels_data', $panels_data ); + update_post_meta( $page_id, '_wp_page_template', siteorigin_panels_setting( 'home-template' ) ); + + if( !empty( $_POST['siteorigin_panels_home_enabled'] ) ) { + update_option('show_on_front', 'page'); + update_option('page_on_front', $page_id); + wp_publish_post($page_id); + } + else { + // We're disabling this home page + if( get_option('page_on_front') == $page_id ) { + // Disable the front page display + update_option('page_on_front', false); + + if( !get_option( 'page_for_posts' ) ) { + update_option( 'show_on_front', 'posts' ); + } + } + + // Change the post status to draft + $post = get_post($page_id); + if($post->post_status != 'draft') { + global $wpdb; + + $wpdb->update( $wpdb->posts, array( 'post_status' => 'draft' ), array( 'ID' => $post->ID ) ); + clean_post_cache( $post->ID ); + + $old_status = $post->post_status; + $post->post_status = 'draft'; + wp_transition_post_status( 'draft', $old_status, $post ); + + do_action( 'edit_post', $post->ID, $post ); + do_action( "save_post_{$post->post_type}", $post->ID, $post, true ); + do_action( 'save_post', $post->ID, $post, true ); + do_action( 'wp_insert_post', $post->ID, $post, true ); + } + + } +} +add_action('admin_init', 'siteorigin_panels_save_home_page'); + +/** + * After the theme is switched, change the template on the home page if the theme supports home page functionality. + */ +function siteorigin_panels_update_home_on_theme_change(){ + if( siteorigin_panels_setting( 'home-page' ) && siteorigin_panels_setting( 'home-template' ) && get_option( 'siteorigin_panels_home_page_id' ) ) { + // Lets update the home page to use the home template that this theme supports + update_post_meta( get_option( 'siteorigin_panels_home_page_id' ), '_wp_page_template', siteorigin_panels_setting( 'home-template' ) ); + } +} +add_action('after_switch_theme', 'siteorigin_panels_update_home_on_theme_change'); + +/** + * @return mixed|void Are we currently viewing the home page + */ +function siteorigin_panels_is_home(){ + $home = ( is_front_page() && is_page() && get_option('show_on_front') == 'page' && get_option('page_on_front') == get_the_ID() && get_post_meta( get_the_ID(), 'panels_data' ) ); + return apply_filters('siteorigin_panels_is_home', $home); +} + +/** + * Check if we're currently viewing a page builder page. + * + * @param bool $can_edit Also check if the user can edit this page + * @return bool + */ +function siteorigin_panels_is_panel($can_edit = false){ + // Check if this is a panel + $is_panel = ( siteorigin_panels_is_home() || ( is_singular() && get_post_meta(get_the_ID(), 'panels_data', false) != '' ) ); + return $is_panel && (!$can_edit || ( (is_singular() && current_user_can('edit_post', get_the_ID())) || ( siteorigin_panels_is_home() && current_user_can('edit_theme_options') ) )); +} + +/** + * Render a panel metabox. + * + * @param $post + */ +function siteorigin_panels_metabox_render( $post ) { + $panels_data = siteorigin_panels_get_current_admin_panels_data(); + include plugin_dir_path(__FILE__) . 'tpl/metabox-panels.php'; +} + +/** + * Enqueue the panels admin scripts + * + * @action admin_print_scripts-post-new.php + * @action admin_print_scripts-post.php + * @action admin_print_scripts-appearance_page_so_panels_home_page + */ +function siteorigin_panels_admin_enqueue_scripts($prefix) { + $screen = get_current_screen(); + + if ( ( $screen->base == 'post' && in_array( $screen->id, siteorigin_panels_setting('post-types') ) ) || $screen->base == 'appearance_page_so_panels_home_page' || $screen->base == 'widgets') { + + wp_enqueue_script( 'so-panels-admin', plugin_dir_url(__FILE__) . 'js/siteorigin-panels.js', array( 'jquery', 'jquery-ui-resizable', 'jquery-ui-sortable', 'jquery-ui-draggable', 'underscore', 'backbone' ), SITEORIGIN_PANELS_VERSION, true ); + wp_enqueue_script( 'so-panels-admin-styles', plugin_dir_url(__FILE__) . 'js/siteorigin-panels-styles.js', array( 'so-panels-admin', 'jquery', 'underscore', 'backbone', 'wp-color-picker' ), SITEORIGIN_PANELS_VERSION, true ); + + if( $screen->base != 'widgets' ) { + // We don't use the history browser and live editor in the widgets interface + wp_enqueue_script( 'so-panels-admin-history', plugin_dir_url(__FILE__) . 'js/siteorigin-panels-history.js', array( 'so-panels-admin', 'jquery', 'underscore', 'backbone' ), SITEORIGIN_PANELS_VERSION, true ); + wp_enqueue_script( 'so-panels-admin-live-editor', plugin_dir_url(__FILE__) . 'js/siteorigin-panels-live-editor.js', array( 'so-panels-admin', 'jquery', 'underscore', 'backbone' ), SITEORIGIN_PANELS_VERSION, true ); + } + + add_action('admin_footer', 'siteorigin_panels_js_templates'); + + $widgets = siteorigin_panels_get_widgets(); + + wp_localize_script( 'so-panels-admin', 'soPanelsOptions', array( + 'widgets' => $widgets, + 'widget_dialog_tabs' => apply_filters( 'siteorigin_panels_widget_dialog_tabs', array( + array( + 'title' => __('All Widgets', 'siteorigin-panels'), + 'filter' => array( 'installed' => true, 'groups' => '' ) + ) + ) ), + 'row_layouts' => apply_filters( 'siteorigin_panels_row_layouts', array() ), + // General localization messages + 'loc' => array( + 'missing_widget' => array( + 'title' => __('Missing Widget', 'siteorigin-panels'), + 'description' => __("Page Builder doesn't know about this widget", 'siteorigin-panels'), + ), + 'time' => array( + 'seconds' => __('%d seconds', 'siteorigin-panels'), + 'minutes' => __('%d minutes', 'siteorigin-panels'), + 'hours' => __('%d hours', 'siteorigin-panels'), + + 'second' => __('%d second', 'siteorigin-panels'), + 'minute' => __('%d minute', 'siteorigin-panels'), + 'hour' => __('%d hour', 'siteorigin-panels'), + + 'ago' => __('%s before', 'siteorigin-panels'), + 'now' => __('Now', 'siteorigin-panels'), + ), + 'history' => array( + // History messages + 'current' => __('Current', 'siteorigin-panels'), + 'revert' => __('Original', 'siteorigin-panels'), + 'restore' => __('Version restored', 'siteorigin-panels'), + + // Widgets + 'widget_deleted' => __('Widget deleted', 'siteorigin-panels'), + 'widget_added' => __('Widget added', 'siteorigin-panels'), + 'widget_edited' => __('Widget edited', 'siteorigin-panels'), + 'widget_duplicated' => __('Widget duplicated', 'siteorigin-panels'), + 'widget_moved' => __('Widget moved', 'siteorigin-panels'), + + // Rows + 'row_deleted' => __('Row deleted', 'siteorigin-panels'), + 'row_added' => __('Row added', 'siteorigin-panels'), + 'row_edited' => __('Row edited', 'siteorigin-panels'), + 'row_moved' => __('Row moved', 'siteorigin-panels'), + 'row_duplicated' => __('Row duplicated', 'siteorigin-panels'), + + // Cells + 'cell_resized' => __('Cell resized', 'siteorigin-panels'), + + // Prebuilt + 'prebuilt_loaded' => __('Prebuilt layout loaded', 'siteorigin-panels'), + ), + + // general localization + 'prebuilt_confirm' => __('Are you sure you want to overwrite your current content? This can be undone in the builder history.', 'siteorigin-panels'), + 'prebuilt_loading' => __('Loading prebuilt layout', 'siteorigin-panels'), + 'confirm_use_builder' => __("Would you like to copy this editor's existing content to Page Builder?", 'siteorigin-panels'), + 'layout_widget' => __('Layout Widget', 'siteorigin-panels'), + 'dropdown_confirm' => __('Are you sure?', 'siteorigin-panels'), + ), + )); + + // Let themes and plugins give names and descriptions to missing widgets. + global $wp_widget_factory; + $missing_widgets = array(); + if ( !empty( $panels_data['widgets'] ) ) { + foreach ( $panels_data['widgets'] as $i => $widget ) { + + // There's a chance the widget was activated by siteorigin_panels_widget_is_missing + if ( empty( $wp_widget_factory->widgets[ $widget['info']['class'] ] ) ) { + $missing_widgets[$widget['info']['class']] = apply_filters('siteorigin_panels_missing_widget_data', array( + 'title' => str_replace( '_', ' ', $widget['info']['class'] ), + 'description' => __('Install the missing widget', 'siteorigin-panels'), + ), $widget['info']['class']); + } + } + } + + if( !empty($missing_widgets) ) { + wp_localize_script( 'so-panels-admin', 'panelsMissingWidgets', $missing_widgets ); + } + + if( $screen->base != 'widgets' ) { + // Render all the widget forms. A lot of widgets use this as a chance to enqueue their scripts + $original_post = isset($GLOBALS['post']) ? $GLOBALS['post'] : null; // Make sure widgets don't change the global post. + foreach($GLOBALS['wp_widget_factory']->widgets as $class => $widget_obj){ + ob_start(); + $widget_obj->form( array() ); + ob_clean(); + } + $GLOBALS['post'] = $original_post; + } + + // This gives panels a chance to enqueue scripts too, without having to check the screen ID. + do_action( 'siteorigin_panel_enqueue_admin_scripts' ); + do_action( 'sidebar_admin_setup' ); + } +} +add_action( 'admin_print_scripts-post-new.php', 'siteorigin_panels_admin_enqueue_scripts' ); +add_action( 'admin_print_scripts-post.php', 'siteorigin_panels_admin_enqueue_scripts' ); +add_action( 'admin_print_scripts-appearance_page_so_panels_home_page', 'siteorigin_panels_admin_enqueue_scripts' ); +add_action( 'admin_print_scripts-widgets.php', 'siteorigin_panels_admin_enqueue_scripts' ); + +/** + * Get an array of all the available widgets. + * + * @return array + */ +function siteorigin_panels_get_widgets(){ + global $wp_widget_factory; + $widgets = array(); + foreach($wp_widget_factory->widgets as $class => $widget_obj) { + $widgets[$class] = array( + 'class' => $class, + 'title' => !empty($widget_obj->name) ? $widget_obj->name : __('Untitled Widget', 'siteorigin-panels'), + 'description' => !empty($widget_obj->widget_options['description']) ? $widget_obj->widget_options['description'] : '', + 'installed' => true, + 'groups' => array(), + ); + + // Get Page Builder specific widget options + if( isset($widget_obj->widget_options['panels_title']) ) { + $widgets[$class]['panels_title'] = $widget_obj->widget_options['panels_title']; + } + if( isset($widget_obj->widget_options['panels_groups']) ) { + $widgets[$class]['groups'] = $widget_obj->widget_options['panels_groups']; + } + if( isset($widget_obj->widget_options['panels_icon']) ) { + $widgets[$class]['icon'] = $widget_obj->widget_options['panels_icon']; + } + + } + + // Other plugins can manipulate the list of widgets. Possibly to add recommended widgets + $widgets = apply_filters('siteorigin_panels_widgets', $widgets); + + // Sort the widgets alphabetically + uasort($widgets, 'siteorigin_panels_widgets_sorter'); + + return $widgets; +} + +/** + * @param $a + * @param $b + */ +function siteorigin_panels_widgets_sorter($a, $b){ + return $a['title'] > $b['title'] ? 1 : -1; +} + +/** + * Display the templates for JS in the footer + */ +function siteorigin_panels_js_templates(){ + include plugin_dir_path(__FILE__).'tpl/js-templates.php'; +} + +/** + * Enqueue the admin panel styles + * + * @action admin_print_styles-post-new.php + * @action admin_print_styles-post.php + */ +function siteorigin_panels_admin_enqueue_styles() { + $screen = get_current_screen(); + if ( in_array( $screen->id, siteorigin_panels_setting('post-types') ) || $screen->base == 'appearance_page_so_panels_home_page' || $screen->base == 'widgets') { + wp_enqueue_style( 'so-panels-admin', plugin_dir_url(__FILE__) . 'css/admin.css', array( 'wp-color-picker' ), SITEORIGIN_PANELS_VERSION ); + do_action( 'siteorigin_panel_enqueue_admin_styles' ); + } +} +add_action( 'admin_print_styles-post-new.php', 'siteorigin_panels_admin_enqueue_styles' ); +add_action( 'admin_print_styles-post.php', 'siteorigin_panels_admin_enqueue_styles' ); +add_action( 'admin_print_styles-appearance_page_so_panels_home_page', 'siteorigin_panels_admin_enqueue_styles' ); +add_action( 'admin_print_styles-widgets.php', 'siteorigin_panels_admin_enqueue_styles' ); + +/** + * Add a help tab to pages with panels. + */ +function siteorigin_panels_add_help_tab($prefix) { + $screen = get_current_screen(); + if( + ( $screen->base == 'post' && ( in_array( $screen->id, siteorigin_panels_setting( 'post-types' ) ) || $screen->id == '') ) + || ($screen->id == 'appearance_page_so_panels_home_page') + ) { + $screen->add_help_tab( array( + 'id' => 'panels-help-tab', //unique id for the tab + 'title' => __( 'Page Builder', 'siteorigin-panels' ), //unique visible title for the tab + 'callback' => 'siteorigin_panels_add_help_tab_content' + ) ); + } +} +add_action('load-page.php', 'siteorigin_panels_add_help_tab', 12); +add_action('load-post-new.php', 'siteorigin_panels_add_help_tab', 12); +add_action('load-appearance_page_so_panels_home_page', 'siteorigin_panels_add_help_tab', 12); + +/** + * Display the content for the help tab. + */ +function siteorigin_panels_add_help_tab_content(){ + include plugin_dir_path(__FILE__) . 'tpl/help.php'; +} + +/** + * Save the panels data + * + * @param $post_id + * @param $post + * + * @action save_post + */ +function siteorigin_panels_save_post( $post_id, $post ) { + if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return; + if ( empty( $_POST['_sopanels_nonce'] ) || !wp_verify_nonce( $_POST['_sopanels_nonce'], 'save' ) ) return; + if ( !current_user_can( 'edit_post', $post_id ) ) return; + + if ( !wp_is_post_revision($post_id) ) { + $panels_data = json_decode( wp_unslash( $_POST['panels_data'] ), true); + $panels_data['widgets'] = siteorigin_panels_process_raw_widgets($panels_data['widgets']); + $panels_data = siteorigin_panels_styles_sanitize_all( $panels_data ); + + if( !empty( $panels_data['widgets'] ) ) { + update_post_meta( $post_id, 'panels_data', $panels_data ); + } + else { + // There are no widgets, so delete the panels data. + delete_post_meta( $post_id, 'panels_data' ); + } + } + else { + // When previewing, we don't need to wp_unslash the panels_data post variable. + $panels_data = json_decode( $_POST['panels_data'], true); + $panels_data['widgets'] = siteorigin_panels_process_raw_widgets($panels_data['widgets']); + $panels_data = siteorigin_panels_styles_sanitize_all( $panels_data ); + + // Because of issue #20299, we are going to save the preview into a different variable so we don't overwrite the actual data. + // https://core.trac.wordpress.org/panels_data/20299 + if( !empty( $panels_data['widgets'] ) ) { + update_post_meta( $post_id, '_panels_data_preview', $panels_data ); + } + } +} +add_action( 'save_post', 'siteorigin_panels_save_post', 10, 2 ); + +/** + * @param $value + * @param $post_id + * @param $meta_key + * + * @return mixed + */ +function siteorigin_panels_view_post_preview($value, $post_id, $meta_key){ + if( $meta_key == 'panels_data' && is_preview() && current_user_can( 'edit_post', $post_id ) ) { + $panels_preview = get_post_meta($post_id, '_panels_data_preview'); + return !empty($panels_preview) ? $panels_preview : $value; + } + + return $value; +} +add_filter('get_post_metadata', 'siteorigin_panels_view_post_preview', 10, 3); + +/** + * Process raw widgets that have come from the Page Builder front end. + * + * @param $widgets + */ +function siteorigin_panels_process_raw_widgets($widgets) { + for($i = 0; $i < count($widgets); $i++) { + + $info = isset($widgets[$i]['panels_info']) ? $widgets[$i]['panels_info'] : $widgets[$i]['info']; + unset($widgets[$i]['info']); + + if( !empty($info['raw']) ) { + if ( class_exists( $info['class'] ) && method_exists( $info['class'], 'update' ) ) { + $the_widget = new $info['class']; + $widgets[$i] = $the_widget->update( $widgets[$i], $widgets[$i] ); + unset($info['raw']); + } + } + + $widgets[$i]['panels_info'] = $info; + + } + + return $widgets; +} + +/** + * Get the home page panels layout data. + * + * @return mixed|void + */ +function siteorigin_panels_get_home_page_data(){ + $panels_data = get_option('siteorigin_panels_home_page', null); + if( is_null( $panels_data ) ){ + // Load the default layout + $layouts = apply_filters( 'siteorigin_panels_prebuilt_layouts', array() ); + $panels_data = !empty($layouts['default_home']) ? $layouts['default_home'] : current($layouts); + } + + return $panels_data; +} + +/** + * Get the Page Builder data for the current admin page. + * + * @return array + */ +function siteorigin_panels_get_current_admin_panels_data(){ + $screen = get_current_screen(); + + // Localize the panels with the panels data + if($screen->base == 'appearance_page_so_panels_home_page'){ + $page_id = get_option( 'siteorigin_panels_home_page_id' ); + if( !empty($page_id) ) $panels_data = get_post_meta( $page_id, 'panels_data', true ); + else $panels_data = null; + + if( is_null( $panels_data ) ){ + // Load the default layout + $layouts = apply_filters( 'siteorigin_panels_prebuilt_layouts', array() ); + + $home_name = siteorigin_panels_setting('home-page-default') ? siteorigin_panels_setting('home-page-default') : 'home'; + $panels_data = !empty($layouts[$home_name]) ? $layouts[$home_name] : current($layouts); + } + + $panels_data = apply_filters( 'siteorigin_panels_data', $panels_data, 'home'); + } + else{ + global $post; + $panels_data = get_post_meta( $post->ID, 'panels_data', true ); + $panels_data = apply_filters( 'siteorigin_panels_data', $panels_data, $post->ID ); + } + + if ( empty( $panels_data ) ) $panels_data = array(); + + return $panels_data; +} + +/** + * Generate the CSS for the page layout. + * + * @param $post_id + * @param $panels_data + * @return string + */ +function siteorigin_panels_generate_css($post_id, $panels_data){ + // Exit if we don't have panels data + if ( empty( $panels_data ) || empty( $panels_data['grids'] ) ) return; + + // Get some of the default settings + $settings = siteorigin_panels_setting(); + $panels_mobile_width = $settings['mobile-width']; + $panels_margin_bottom = $settings['margin-bottom']; + + $css = new SiteOrigin_Panels_Css_Builder(); + + $ci = 0; + foreach ( $panels_data['grids'] as $gi => $grid ) { + + $cell_count = intval( $grid['cells'] ); + + // Add the cell sizing + for ( $i = 0; $i < $cell_count; $i++ ) { + $cell = $panels_data['grid_cells'][$ci++]; + + if ( $cell_count > 1 ) { + $width = round( $cell['weight'] * 100, 3 ) . '%'; + $width = apply_filters('siteorigin_panels_css_cell_width', $width, $grid, $gi, $cell, $ci - 1, $panels_data, $post_id); + + // Add the width and ensure we have correct formatting for CSS. + $css->add_cell_css($post_id, $gi, $i, '', array( + 'width' => str_replace(',', '.', $width) + )); + } + } + + // Add the bottom margin to any grids that aren't the last + if($gi != count($panels_data['grids'])-1){ + // Filter the bottom margin for this row with the arguments + $css->add_row_css($post_id, $gi, '', array( + 'margin-bottom' => apply_filters('siteorigin_panels_css_row_margin_bottom', $panels_margin_bottom.'px', $grid, $gi, $panels_data, $post_id) + )); + } + + if ( $cell_count > 1 ) { + $css->add_cell_css($post_id, $gi, false, '', array( + // Float right for RTL + 'float' => !is_rtl() ? 'left' : 'right' + )); + } + + if ( $settings['responsive'] ) { + // Mobile Responsive + $css->add_cell_css($post_id, $gi, false, '', array( + 'float' => 'none', + 'width' => 'auto' + ), $panels_mobile_width); + + for ( $i = 0; $i < $cell_count; $i++ ) { + if ( $i != $cell_count - 1 ) { + $css->add_cell_css($post_id, $gi, $i, '', array( + 'margin-bottom' => $panels_margin_bottom . 'px', + ), $panels_mobile_width); + } + } + } + } + + if( $settings['responsive'] ) { + // Add CSS to prevent overflow on mobile resolution. + $css->add_row_css($post_id, false, '', array( + 'margin-left' => 0, + 'margin-right' => 0, + ), $panels_mobile_width); + + $css->add_cell_css($post_id, false, false, '', array( + 'padding' => 0, + ), $panels_mobile_width); + } + + // Add the bottom margins + $css->add_cell_css($post_id, false, false, '.panel', array( + 'margin-bottom' => $panels_margin_bottom.'px' + )); + $css->add_cell_css($post_id, false, false, '.panel:last-child', array( + 'margin-bottom' => 0 + )); + + // Let other plugins customize various aspects of the rows (grids) + foreach ( $panels_data['grids'] as $gi => $grid ) { + // Rows with only one cell don't need gutters + if($grid['cells'] <= 1) continue; + + // Let other themes and plugins change the gutter. + $gutter = apply_filters('siteorigin_panels_css_row_gutter', $settings['margin-sides'].'px', $grid, $gi, $panels_data); + + if( !empty($gutter) ) { + // We actually need to find half the gutter. + preg_match('/([0-9\.,]+)(.*)/', $gutter, $match); + if( !empty( $match[1] ) ) { + $margin_half = (floatval($match[1])/2) . $match[2]; + $css->add_row_css($post_id, $gi, '', array( + 'margin-left' => '-' . $margin_half, + 'margin-right' => '-' . $margin_half, + ) ); + $css->add_cell_css($post_id, $gi, false, '', array( + 'padding-left' => $margin_half, + 'padding-right' => $margin_half, + ) ); + + } + } + } + + // Let other plugins and components filter the CSS object. + $css = apply_filters('siteorigin_panels_css_object', $css, $panels_data, $post_id); + return $css->get_css(); +} + +/** + * Prepare the content of the page early on so widgets can enqueue their scripts and styles + */ +function siteorigin_panels_prepare_single_post_content(){ + if( is_singular() ) { + global $siteorigin_panels_cache; + if( empty($siteorigin_panels_cache[ get_the_ID() ] ) ) { + $siteorigin_panels_cache[ get_the_ID() ] = siteorigin_panels_render( get_the_ID() ); + } + } +} +add_action('wp_enqueue_scripts', 'siteorigin_panels_prepare_single_post_content'); + +/** + * Filter the content of the panel, adding all the widgets. + * + * @param $content + * @return string + * + * @filter the_content + */ +function siteorigin_panels_filter_content( $content ) { + global $post; + + if ( empty( $post ) ) return $content; + if ( !apply_filters( 'siteorigin_panels_filter_content_enabled', true ) ) return $content; + if ( in_array( $post->post_type, siteorigin_panels_setting('post-types') ) ) { + $panel_content = siteorigin_panels_render( $post->ID ); + + if ( !empty( $panel_content ) ) $content = $panel_content; + } + + return $content; +} +add_filter( 'the_content', 'siteorigin_panels_filter_content' ); + + +/** + * Render the panels + * + * @param int|string|bool $post_id The Post ID or 'home'. + * @param bool $enqueue_css Should we also enqueue the layout CSS. + * @param array|bool $panels_data Existing panels data. By default load from settings or post meta. + * @return string + */ +function siteorigin_panels_render( $post_id = false, $enqueue_css = true, $panels_data = false ) { + if( empty($post_id) ) $post_id = get_the_ID(); + + global $siteorigin_panels_current_post; + $old_current_post = $siteorigin_panels_current_post; + $siteorigin_panels_current_post = $post_id; + + // Try get the cached panel from in memory cache. + global $siteorigin_panels_cache; + if(!empty($siteorigin_panels_cache) && !empty($siteorigin_panels_cache[$post_id])) + return $siteorigin_panels_cache[$post_id]; + + if( empty($panels_data) ) { + if( strpos($post_id, 'prebuilt:') === 0) { + list($null, $prebuilt_id) = explode(':', $post_id, 2); + $layouts = apply_filters('siteorigin_panels_prebuilt_layouts', array()); + $panels_data = !empty($layouts[$prebuilt_id]) ? $layouts[$prebuilt_id] : array(); + } + else if($post_id == 'home'){ + $panels_data = get_post_meta( get_option('siteorigin_panels_home_page_id'), 'panels_data', true ); + + if( is_null($panels_data) ){ + // Load the default layout + $layouts = apply_filters('siteorigin_panels_prebuilt_layouts', array()); + $prebuilt_id = siteorigin_panels_setting('home-page-default') ? siteorigin_panels_setting('home-page-default') : 'home'; + + $panels_data = !empty($layouts[$prebuilt_id]) ? $layouts[$prebuilt_id] : current($layouts); + } + } + else{ + if ( post_password_required($post_id) ) return false; + $panels_data = get_post_meta( $post_id, 'panels_data', true ); + } + } + + $panels_data = apply_filters( 'siteorigin_panels_data', $panels_data, $post_id ); + if( empty( $panels_data ) || empty( $panels_data['grids'] ) ) return ''; + + if( is_rtl() ) $panels_data = siteorigin_panels_make_rtl( $panels_data ); + + // Create the skeleton of the grids + $grids = array(); + if( !empty( $panels_data['grids'] ) && !empty( $panels_data['grids'] ) ) { + foreach ( $panels_data['grids'] as $gi => $grid ) { + $gi = intval( $gi ); + $grids[$gi] = array(); + for ( $i = 0; $i < $grid['cells']; $i++ ) { + $grids[$gi][$i] = array(); + } + } + } + + // We need this to migrate from the old $panels_data that put widget meta into the "info" key instead of "panels_info" + if( !empty( $panels_data['widgets'] ) && is_array($panels_data['widgets']) ) { + foreach ( $panels_data['widgets'] as $i => $widget ) { + if( empty( $panels_data['widgets'][$i]['panels_info'] ) ) { + $panels_data['widgets'][$i]['panels_info'] = $panels_data['widgets'][$i]['info']; + unset($panels_data['widgets'][$i]['info']); + } + } + } + + if( !empty( $panels_data['widgets'] ) && is_array($panels_data['widgets']) ){ + foreach ( $panels_data['widgets'] as $widget ) { + // Put the widgets in the grids + $grids[ intval( $widget['panels_info']['grid']) ][ intval( $widget['panels_info']['cell'] ) ][] = $widget; + } + } + + ob_start(); + + // Add the panel layout wrapper + echo '
'; + + global $siteorigin_panels_inline_css; + if( empty($siteorigin_panels_inline_css) ) $siteorigin_panels_inline_css = ''; + + if($enqueue_css) { + wp_enqueue_style('siteorigin-panels-front'); + $siteorigin_panels_inline_css .= siteorigin_panels_generate_css($post_id, $panels_data); + } + + foreach ( $grids as $gi => $cells ) { + + $grid_classes = apply_filters( 'siteorigin_panels_row_classes', array('panel-grid'), $panels_data['grids'][$gi] ); + $grid_attributes = apply_filters( 'siteorigin_panels_row_attributes', array( + 'class' => implode( ' ', $grid_classes ), + 'id' => 'pg-' . $post_id . '-' . $gi + ), $panels_data['grids'][$gi] ); + + // This allows other themes and plugins to add html before the row + echo apply_filters( 'siteorigin_panels_before_row', '', $panels_data['grids'][$gi], $grid_attributes ); + + echo '
$value ) { + echo $name.'="'.esc_attr($value).'" '; + } + echo '>'; + + $style_attributes = array(); + if( !empty( $panels_data['grids'][$gi]['style']['class'] ) ) { + $style_attributes['class'] = array('panel-row-style-'.$panels_data['grids'][$gi]['style']['class']); + } + + // Themes can add their own attributes to the style wrapper + $row_style_wrapper = siteorigin_panels_start_style_wrapper( 'row', $style_attributes, !empty($panels_data['grids'][$gi]['style']) ? $panels_data['grids'][$gi]['style'] : array() ); + if( !empty($row_style_wrapper) ) echo $row_style_wrapper; + + foreach ( $cells as $ci => $widgets ) { + // Themes can add their own styles to cells + $cell_classes = apply_filters( 'siteorigin_panels_row_cell_classes', array('panel-grid-cell'), $panels_data ); + $cell_attributes = apply_filters( 'siteorigin_panels_row_cell_attributes', array( + 'class' => implode( ' ', $cell_classes ), + 'id' => 'pgc-' . $post_id . '-' . $gi . '-' . $ci + ), $panels_data ); + + echo '
$value ) { + echo $name.'="'.esc_attr($value).'" '; + } + echo '>'; + + $cell_style_wrapper = siteorigin_panels_start_style_wrapper( 'cell', array(), !empty($panels_data['grids'][$gi]['style']) ? $panels_data['grids'][$gi]['style'] : array() ); + if( !empty($cell_style_wrapper) ) echo $cell_style_wrapper; + + foreach ( $widgets as $pi => $widget_info ) { + $instance = $widget_info; + unset( $instance['panels_info'] ); + + // TODO this wrapper should go in the before/after widget arguments + $widget_style_wrapper = siteorigin_panels_start_style_wrapper( 'widget', array(), !empty( $widget_info['panels_info']['style'] ) ? $widget_info['panels_info']['style'] : array() ); + siteorigin_panels_the_widget( $widget_info['panels_info']['class'], $instance, $gi, $ci, $pi, $pi == 0, $pi == count( $widgets ) - 1, $post_id, $widget_style_wrapper ); + } + if ( empty( $widgets ) ) echo ' '; + + if( !empty($cell_style_wrapper) ) echo '
'; + echo '
'; + } + echo '
'; + + // Close the + if( !empty($row_style_wrapper) ) echo '
'; + + // This allows other themes and plugins to add html after the row + echo apply_filters( 'siteorigin_panels_after_row', '', $panels_data['grids'][$gi], $grid_attributes ); + } + + echo '
'; + + $html = ob_get_clean(); + + // Reset the current post + $siteorigin_panels_current_post = $old_current_post; + + return apply_filters( 'siteorigin_panels_render', $html, $post_id, !empty($post) ? $post : null ); +} + +/** + * Echo the style wrapper and return if there was a wrapper + * + * @param $name + * @param $style_attributes + * @param array $style_args + * + * @return bool Is there a style wrapper + */ +function siteorigin_panels_start_style_wrapper($name, $style_attributes, $style_args = array()){ + + $style_wrapper = ''; + + if( empty($style_attributes['class']) ) $style_attributes['class'] = array(); + if( empty($style_attributes['style']) ) $style_attributes['style'] = ''; + + $style_attributes = apply_filters('siteorigin_panels_' . $name . '_style_attributes', $style_attributes, $style_args ); + + if( empty($style_attributes['class']) ) unset($style_attributes['class']); + if( empty($style_attributes['style']) ) unset($style_attributes['style']); + + if( !empty($style_attributes) ) { + if(empty($style_attributes['class'])) $style_attributes['class'] = array(); + $style_attributes['class'][] = 'panel-' . $name . '-style'; + $style_attributes['class'] = array_unique( $style_attributes['class'] ); + + // Filter and sanitize the classes + $style_attributes['class'] = apply_filters('siteorigin_panels_' . $name . '_style_classes', $style_attributes['class'], $style_attributes, $style_args); + $style_attributes['class'] = array_map('sanitize_html_class', $style_attributes['class']); + + $style_wrapper = '
$value ) { + if( is_array($value) ) { + $style_wrapper .= $name.'="'.esc_attr( implode( " ", array_unique( $value ) ) ).'" '; + } + else { + $style_wrapper .= $name.'="'.esc_attr($value).'" '; + } + } + $style_wrapper .= '>'; + + return $style_wrapper; + } + + return $style_wrapper; +} + +/** + * Print inline CSS in the header and footer. + */ +function siteorigin_panels_print_inline_css(){ + global $siteorigin_panels_inline_css; + if(!empty($siteorigin_panels_inline_css)) { + ?>widgets[$widget]) ? $wp_widget_factory->widgets[$widget] : false; + $the_widget = apply_filters( 'siteorigin_panels_widget_object', $the_widget, $widget, $instance ); + + if( empty($post_id) ) $post_id = get_the_ID(); + + $classes = array( 'panel', 'widget' ); + if ( !empty( $the_widget ) && !empty( $the_widget->id_base ) ) $classes[] = 'widget_' . $the_widget->id_base; + if ( $is_first ) $classes[] = 'panel-first-child'; + if ( $is_last ) $classes[] = 'panel-last-child'; + $id = 'panel-' . $post_id . '-' . $grid . '-' . $cell . '-' . $panel; + + // Filter and sanitize the classes + $classes = apply_filters('siteorigin_panels_widget_classes', $classes, $widget, $instance); + $classes = array_map('sanitize_html_class', $classes); + + $args = array( + 'before_widget' => '
', + 'after_widget' => '
', + 'before_title' => '

', + 'after_title' => '

', + 'widget_id' => 'widget-' . $grid . '-' . $cell . '-' . $panel + ); + + // If there is a style wrapper, add it. + if( !empty($style_wrapper) ) { + $args['before_widget'] = $args['before_widget'] . $style_wrapper; + $args['after_widget'] = '
' . $args['after_widget']; + } + + if ( !empty($the_widget) && is_a($the_widget, 'WP_Widget') ) { + $the_widget->widget($args , $instance ); + } + else { + // This gives themes a chance to display some sort of placeholder for missing widgets + echo apply_filters('siteorigin_panels_missing_widget', '', $widget, $args , $instance); + } +} + +/** + * Add the Edit Home Page item to the admin bar. + * + * @param WP_Admin_Bar $admin_bar + * @return WP_Admin_Bar + */ +function siteorigin_panels_admin_bar_menu($admin_bar){ + // Ignore this unless the theme is using the home page feature. + if( !siteorigin_panels_setting('home-page') ) return $admin_bar; + if( !current_user_can('edit_theme_options') ) return $admin_bar; + + if( is_home() || is_front_page() ) { + if( ( is_page() && get_the_ID() == get_option('siteorigin_panels_home_page_id') ) || current_user_can('edit_theme_options') ) { + $admin_bar->add_node( array( + 'id' => 'edit-home-page', + 'title' => __('Edit Home Page', 'siteorigin-panels'), + 'href' => admin_url('themes.php?page=so_panels_home_page') + ) ); + } + + if( is_page() && get_the_ID() == get_option('siteorigin_panels_home_page_id') ) { + $admin_bar->remove_node('edit'); + } + } + + return $admin_bar; +} +add_action('admin_bar_menu', 'siteorigin_panels_admin_bar_menu', 100); + +/** + * Handles creating the preview. + */ +function siteorigin_panels_preview(){ + if(isset($_GET['siteorigin_panels_preview']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'siteorigin-panels-preview')){ + global $siteorigin_panels_is_preview; + $siteorigin_panels_is_preview = true; + // Set the panels home state to true + if(empty($_POST['post_id'])) $GLOBALS['siteorigin_panels_is_panels_home'] = true; + add_action('siteorigin_panels_data', 'siteorigin_panels_home_preview_load_data'); + locate_template( siteorigin_panels_setting('home-template'), true ); + exit(); + } +} +add_action('template_redirect', 'siteorigin_panels_preview'); + +/** + * Is this a preview. + * + * @return bool + */ +function siteorigin_panels_is_preview(){ + global $siteorigin_panels_is_preview; + return (bool) $siteorigin_panels_is_preview; +} + +/** + * Hide the admin bar for panels previews. + * + * @param $show + * @return bool + */ +function siteorigin_panels_preview_adminbar($show){ + if(!$show) return false; + return !(isset($_GET['siteorigin_panels_preview']) && wp_verify_nonce($_GET['_wpnonce'], 'siteorigin-panels-preview')); +} +add_filter('show_admin_bar', 'siteorigin_panels_preview_adminbar'); + +/** + * This is a way to show previews of panels, especially for the home page. + * + * @param $val + * @return array + */ +function siteorigin_panels_home_preview_load_data($val){ + if( isset($_GET['siteorigin_panels_preview']) ){ + $val = siteorigin_panels_get_panels_data_from_post( $_POST ); + } + + return $val; +} + +/** + * Add all the necessary body classes. + * + * @param $classes + * @return array + */ +function siteorigin_panels_body_class($classes){ + if( siteorigin_panels_is_panel() ) $classes[] = 'siteorigin-panels'; + if( siteorigin_panels_is_home() ) $classes[] = 'siteorigin-panels-home'; + + if(isset($_GET['siteorigin_panels_preview']) && isset($_GET['_wpnonce']) && wp_verify_nonce($_GET['_wpnonce'], 'siteorigin-panels-preview')) { + // This is a home page preview + $classes[] = 'siteorigin-panels'; + $classes[] = 'siteorigin-panels-home'; + } + + return $classes; +} +add_filter('body_class', 'siteorigin_panels_body_class'); + +/** + * Enqueue the required styles + */ +function siteorigin_panels_enqueue_styles(){ + wp_register_style('siteorigin-panels-front', plugin_dir_url(__FILE__) . 'css/front.css', array(), SITEORIGIN_PANELS_VERSION ); +} +add_action('wp_enqueue_scripts', 'siteorigin_panels_enqueue_styles', 1); + +/** + * Add a filter to import panels_data meta key. This fixes serialized PHP. + */ +function siteorigin_panels_wp_import_post_meta($post_meta){ + foreach($post_meta as $i => $meta) { + if($meta['key'] == 'panels_data') { + $value = $meta['value']; + $value = preg_replace("/[\r\n]/", "<<
>>", $value); + $value = preg_replace('!s:(\d+):"(.*?)";!e', "'s:'.strlen('$2').':\"$2\";'", $value); + $value = unserialize($value); + $value = array_map('siteorigin_panels_wp_import_post_meta_map', $value); + + $post_meta[$i]['value'] = $value; + } + } + + return $post_meta; +} +add_filter('wp_import_post_meta', 'siteorigin_panels_wp_import_post_meta'); + +/** + * A callback that replaces temporary break tag with actual line breaks. + * + * @param $val + * @return array|mixed + */ +function siteorigin_panels_wp_import_post_meta_map($val) { + if(is_string($val)) return str_replace('<<
>>', "\n", $val); + else return array_map('siteorigin_panels_wp_import_post_meta_map', $val); +} + +/** + * Render a widget form with all the Page Builder specific fields + * + * @param string $widget The class of the widget + * @param array $instance Widget values + * @param bool $raw + * @return mixed|string The form + */ +function siteorigin_panels_render_form($widget, $instance = array(), $raw = false){ + global $wp_widget_factory; + + // This is a chance for plugins to replace missing widgets + $the_widget = !empty($wp_widget_factory->widgets[$widget]) ? $wp_widget_factory->widgets[$widget] : false; + $the_widget = apply_filters( 'siteorigin_panels_widget_object', $the_widget, $widget ); + + if ( empty($the_widget) || !is_a( $the_widget, 'WP_Widget' ) ) { + $widgets = siteorigin_panels_get_widgets(); + + if( !empty($widgets[$widget]) && !empty( $widgets[$widget]['plugin'] ) ) { + // We know about this widget, show a form about installing it. + $install_url = siteorigin_panels_plugin_activation_install_url($widgets[$widget]['plugin']['slug'], $widgets[$widget]['plugin']['name']); + $form = + '
' . + '

' . sprintf( + __("You need to install %s to use the widget %s. It's a free plugin available off the official WordPress plugin directory.", 'siteorigin-panels'), + $install_url, + $widgets[$widget]['plugin']['name'], + $widget + ). '

' . + '

' . __("Save and reload this page to start using the widget after you've installed it.") . '

' . + '
'; + } + else { + // This widget is missing, so show a missing widgets form. + $form = + '

' . + sprintf( + __('The widget %s is not available. Please try locate and install the missing plugin. Post on the support forums if you need help.', 'siteorigin-panels'), + $widget, + 'http://siteorigin.com/thread/' + ). + '

'; + } + + // Allow other themes and plugins to change the missing widget form + return apply_filters('siteorigin_panels_missing_widget_form', $form, $widget, $instance); + } + + if( $raw ) $instance = $the_widget->update($instance, $instance); + + $the_widget->id = 'temp'; + $the_widget->number = '{$id}'; + + ob_start(); + $the_widget->form($instance); + $form = ob_get_clean(); + + // Convert the widget field naming into ones that Page Builder uses + $exp = preg_quote( $the_widget->get_field_name('____') ); + $exp = str_replace('____', '(.*?)', $exp); + $form = preg_replace( '/'.$exp.'/', 'widgets[{$id}][$1]', $form ); + + $form = apply_filters('siteorigin_panels_widget_form', $form, $widget, $instance); + + // Add all the information fields + return $form; +} + +/** + * This takes existing Page Builder data and makes it RTL by reversing the content + */ +function siteorigin_panels_make_rtl($panels_data){ + + // To start, we need a cell count for every row + foreach($panels_data['widgets'] as &$widget) { + // This reverses the cells of the widgets + $count = $panels_data['grids'][ $widget['panels_info']['grid'] ]['cells']; + $widget['panels_info']['cell'] = abs( $widget['panels_info']['cell'] - $count + 1 ); + } + + // Now we need to swap around the grid cells because we're going to use float right instead. + $grid_cells = array(); + foreach( $panels_data['grid_cells'] as $cell) { + if( empty( $grid_cells[ $cell['grid'] ] ) ) $grid_cells[ $cell['grid'] ] = array(); + array_unshift( $grid_cells[ $cell['grid'] ], $cell ); + } + $new_grid_cells = array(); + foreach( $grid_cells as $i => $cells ) { + foreach($cells as $cell) { + $new_grid_cells[] = $cell; + } + } + $panels_data['grid_cells'] = $new_grid_cells; + + return $panels_data; +} + +/** + * Add action links to the plugin list for Page Builder. + * + * @param $links + * @return array + */ +function siteorigin_panels_plugin_action_links($links) { + $links[] = '' . __('Support Forum', 'siteorigin-panels') . ''; + $links[] = '' . __('Newsletter', 'siteorigin-panels') . ''; + return $links; +} +add_action('plugin_action_links_' . plugin_basename(__FILE__), 'siteorigin_panels_plugin_action_links'); + +// Include the live editor file if we're in live editor mode. +if( !empty( $_GET['siteorigin_panels_live_editor'] ) ) require_once plugin_dir_path(__FILE__) . 'inc/live-editor.php'; \ No newline at end of file diff --git a/tpl/admin-home-page.php b/tpl/admin-home-page.php new file mode 100644 index 000000000..f4253c650 --- /dev/null +++ b/tpl/admin-home-page.php @@ -0,0 +1,39 @@ + + +
+
+

+

+ + + + + + + +

+ + +
+

View page', 'siteorigin-panels'), get_home_url() ) ?>

+
+ + +
+ +
+ + + +

+ + +
+ +
\ No newline at end of file diff --git a/tpl/help.php b/tpl/help.php new file mode 100644 index 000000000..3cdfe6b6a --- /dev/null +++ b/tpl/help.php @@ -0,0 +1,15 @@ +

+ + +

+

+ full documentation on SiteOrigin.", 'siteorigin-panels' ) . ' ' . + __( "Ask a question on our support forum if you need help and sign up to our newsletter to stay up to date with future developments.", 'siteorigin-panels' ), + 'http://siteorigin.com/page-builder/documentation/' , + 'http://siteorigin.com/threads/plugin-page-builder/', + 'http://siteorigin.com/#newsletter' + ); + ?> +

\ No newline at end of file diff --git a/tpl/js-templates.php b/tpl/js-templates.php new file mode 100644 index 000000000..4d879d1a7 --- /dev/null +++ b/tpl/js-templates.php @@ -0,0 +1,435 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tpl/metabox-panels.php b/tpl/metabox-panels.php new file mode 100644 index 000000000..9fe115e2e --- /dev/null +++ b/tpl/metabox-panels.php @@ -0,0 +1,8 @@ +
+ + + + + + +
\ No newline at end of file diff --git a/tpl/options.php b/tpl/options.php new file mode 100644 index 000000000..60edd0c8f --- /dev/null +++ b/tpl/options.php @@ -0,0 +1,97 @@ + + +
+

+

+ +
+ +
+ +

+ + + + + + + + + + +
+ +
+ +

+ + + + + + +
+ + + +

+ +

+ +
+
\ No newline at end of file diff --git a/video/jplayer/Jplayer.swf b/video/jplayer/Jplayer.swf new file mode 100755 index 0000000000000000000000000000000000000000..2121715c8fa1314942889291905cd6da37c7c2ca GIT binary patch literal 14148 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 literal 0 HcmV?d00001 diff --git a/video/jplayer/jquery.jplayer.min.js b/video/jplayer/jquery.jplayer.min.js new file mode 100755 index 000000000..796e970e2 --- /dev/null +++ b/video/jplayer/jquery.jplayer.min.js @@ -0,0 +1,114 @@ +/* + * jPlayer Plugin for jQuery JavaScript Library + * http://www.jplayer.org + * + * Copyright (c) 2009 - 2013 Happyworm Ltd + * Licensed under the MIT license. + * http://opensource.org/licenses/MIT + * + * Author: Mark J Panaghiston + * Version: 2.5.0 + * Date: 7th November 2013 + */ + +(function(b,f){"function"===typeof define&&define.amd?define(["jquery"],f):b.jQuery?f(b.jQuery):f(b.Zepto)})(this,function(b,f){b.fn.jPlayer=function(a){var c="string"===typeof a,d=Array.prototype.slice.call(arguments,1),e=this;a=!c&&d.length?b.extend.apply(null,[!0,a].concat(d)):a;if(c&&"_"===a.charAt(0))return e;c?this.each(function(){var c=b(this).data("jPlayer"),h=c&&b.isFunction(c[a])?c[a].apply(c,d):c;if(h!==c&&h!==f)return e=h,!1}):this.each(function(){var c=b(this).data("jPlayer");c?c.option(a|| +{}):b(this).data("jPlayer",new b.jPlayer(a,this))});return e};b.jPlayer=function(a,c){if(arguments.length){this.element=b(c);this.options=b.extend(!0,{},this.options,a);var d=this;this.element.bind("remove.jPlayer",function(){d.destroy()});this._init()}};"function"!==typeof b.fn.stop&&(b.fn.stop=function(){});b.jPlayer.emulateMethods="load play pause";b.jPlayer.emulateStatus="src readyState networkState currentTime duration paused ended playbackRate";b.jPlayer.emulateOptions="muted volume";b.jPlayer.reservedEvent= +"ready flashreset resize repeat error warning";b.jPlayer.event={};b.each("ready flashreset resize repeat click error warning loadstart progress suspend abort emptied stalled play pause loadedmetadata loadeddata waiting playing canplay canplaythrough seeking seeked timeupdate ended ratechange durationchange volumechange".split(" "),function(){b.jPlayer.event[this]="jPlayer_"+this});b.jPlayer.htmlEvent="loadstart abort emptied stalled loadedmetadata loadeddata canplay canplaythrough".split(" ");b.jPlayer.pause= +function(){b.each(b.jPlayer.prototype.instances,function(a,c){c.data("jPlayer").status.srcSet&&c.jPlayer("pause")})};b.jPlayer.timeFormat={showHour:!1,showMin:!0,showSec:!0,padHour:!1,padMin:!0,padSec:!0,sepHour:":",sepMin:":",sepSec:""};var m=function(){this.init()};m.prototype={init:function(){this.options={timeFormat:b.jPlayer.timeFormat}},time:function(a){var c=new Date(1E3*(a&&"number"===typeof a?a:0)),b=c.getUTCHours();a=this.options.timeFormat.showHour?c.getUTCMinutes():c.getUTCMinutes()+60* +b;c=this.options.timeFormat.showMin?c.getUTCSeconds():c.getUTCSeconds()+60*a;b=this.options.timeFormat.padHour&&10>b?"0"+b:b;a=this.options.timeFormat.padMin&&10>a?"0"+a:a;c=this.options.timeFormat.padSec&&10>c?"0"+c:c;b=""+(this.options.timeFormat.showHour?b+this.options.timeFormat.sepHour:"");b+=this.options.timeFormat.showMin?a+this.options.timeFormat.sepMin:"";return b+=this.options.timeFormat.showSec?c+this.options.timeFormat.sepSec:""}};var n=new m;b.jPlayer.convertTime=function(a){return n.time(a)}; +b.jPlayer.uaBrowser=function(a){a=a.toLowerCase();var c=/(opera)(?:.*version)?[ \/]([\w.]+)/,b=/(msie) ([\w.]+)/,e=/(mozilla)(?:.*? rv:([\w.]+))?/;a=/(webkit)[ \/]([\w.]+)/.exec(a)||c.exec(a)||b.exec(a)||0>a.indexOf("compatible")&&e.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}};b.jPlayer.uaPlatform=function(a){var c=a.toLowerCase(),b=/(android)/,e=/(mobile)/;a=/(ipad|iphone|ipod|android|blackberry|playbook|windows ce|webos)/.exec(c)||[];c=/(ipad|playbook)/.exec(c)||!e.exec(c)&&b.exec(c)|| +[];a[1]&&(a[1]=a[1].replace(/\s/g,"_"));return{platform:a[1]||"",tablet:c[1]||""}};b.jPlayer.browser={};b.jPlayer.platform={};var k=b.jPlayer.uaBrowser(navigator.userAgent);k.browser&&(b.jPlayer.browser[k.browser]=!0,b.jPlayer.browser.version=k.version);k=b.jPlayer.uaPlatform(navigator.userAgent);k.platform&&(b.jPlayer.platform[k.platform]=!0,b.jPlayer.platform.mobile=!k.tablet,b.jPlayer.platform.tablet=!!k.tablet);b.jPlayer.getDocMode=function(){var a;b.jPlayer.browser.msie&&(document.documentMode? +a=document.documentMode:(a=5,document.compatMode&&"CSS1Compat"===document.compatMode&&(a=7)));return a};b.jPlayer.browser.documentMode=b.jPlayer.getDocMode();b.jPlayer.nativeFeatures={init:function(){var a=document,c=a.createElement("video"),b={w3c:"fullscreenEnabled fullscreenElement requestFullscreen exitFullscreen fullscreenchange fullscreenerror".split(" "),moz:"mozFullScreenEnabled mozFullScreenElement mozRequestFullScreen mozCancelFullScreen mozfullscreenchange mozfullscreenerror".split(" "), +webkit:" webkitCurrentFullScreenElement webkitRequestFullScreen webkitCancelFullScreen webkitfullscreenchange ".split(" "),webkitVideo:"webkitSupportsFullscreen webkitDisplayingFullscreen webkitEnterFullscreen webkitExitFullscreen ".split(" ")},e=["w3c","moz","webkit","webkitVideo"],g,h;this.fullscreen=c={support:{w3c:!!a[b.w3c[0]],moz:!!a[b.moz[0]],webkit:"function"===typeof a[b.webkit[3]],webkitVideo:"function"===typeof c[b.webkitVideo[2]]},used:{}};g=0;for(h=e.length;g','','','',''];c=document.createElement(''); +for(var e=0;e").join(">").split('"').join(""")},_qualifyURL:function(a){var c=document.createElement("div");c.innerHTML='x';return c.firstChild.href},_absoluteMediaUrls:function(a){var c=this;b.each(a,function(b,e){c.format[b]&& +(a[b]=c._qualifyURL(e))});return a},setMedia:function(a){var c=this,d=!1,e=this.status.media.poster!==a.poster;this._resetMedia();this._resetGate();this._resetActive();a=this._absoluteMediaUrls(a);b.each(this.formats,function(e,f){var k="video"===c.format[f].media;b.each(c.solutions,function(b,e){if(c[e].support[f]&&c._validString(a[f])){var g="html"===e;k?(g?(c.html.video.gate=!0,c._html_setVideo(a),c.html.active=!0):(c.flash.gate=!0,c._flash_setVideo(a),c.flash.active=!0),c.css.jq.videoPlay.length&& +c.css.jq.videoPlay.show(),c.status.video=!0):(g?(c.html.audio.gate=!0,c._html_setAudio(a),c.html.active=!0):(c.flash.gate=!0,c._flash_setAudio(a),c.flash.active=!0),c.css.jq.videoPlay.length&&c.css.jq.videoPlay.hide(),c.status.video=!1);d=!0;return!1}});if(d)return!1});d?(this.status.nativeVideoControls&&this.html.video.gate||!this._validString(a.poster)||(e?this.htmlElement.poster.src=a.poster:this.internal.poster.jq.show()),this.status.srcSet=!0,this.status.media=b.extend({},a),this._updateButtons(!1), +this._updateInterface()):this._error({type:b.jPlayer.error.NO_SUPPORT,context:"{supplied:'"+this.options.supplied+"'}",message:b.jPlayer.errorMsg.NO_SUPPORT,hint:b.jPlayer.errorHint.NO_SUPPORT})},_resetMedia:function(){this._resetStatus();this._updateButtons(!1);this._updateInterface();this._seeked();this.internal.poster.jq.hide();clearTimeout(this.internal.htmlDlyCmdId);this.html.active?this._html_resetMedia():this.flash.active&&this._flash_resetMedia()},clearMedia:function(){this._resetMedia(); +this.html.active?this._html_clearMedia():this.flash.active&&this._flash_clearMedia();this._resetGate();this._resetActive()},load:function(){this.status.srcSet?this.html.active?this._html_load():this.flash.active&&this._flash_load():this._urlNotSetError("load")},focus:function(){this.options.keyEnabled&&(b.jPlayer.focus=this)},play:function(a){a="number"===typeof a?a:NaN;this.status.srcSet?(this.focus(),this.html.active?this._html_play(a):this.flash.active&&this._flash_play(a)):this._urlNotSetError("play")}, +videoPlay:function(){this.play()},pause:function(a){a="number"===typeof a?a:NaN;this.status.srcSet?this.html.active?this._html_pause(a):this.flash.active&&this._flash_pause(a):this._urlNotSetError("pause")},tellOthers:function(a,c){var d=this,e="function"===typeof c,g=Array.prototype.slice.call(arguments);"string"===typeof a&&(e&&g.splice(1,1),b.each(this.instances,function(){d.element!==this&&(e&&!c.call(this.data("jPlayer"),d)||this.jPlayer.apply(this,g))}))},pauseOthers:function(a){this.tellOthers("pause", +function(){return this.status.srcSet},a)},stop:function(){this.status.srcSet?this.html.active?this._html_pause(0):this.flash.active&&this._flash_pause(0):this._urlNotSetError("stop")},playHead:function(a){a=this._limitValue(a,0,100);this.status.srcSet?this.html.active?this._html_playHead(a):this.flash.active&&this._flash_playHead(a):this._urlNotSetError("playHead")},_muted:function(a){this.mutedWorker(a);this.options.globalVolume&&this.tellOthers("mutedWorker",function(){return this.options.globalVolume}, +a)},mutedWorker:function(a){this.options.muted=a;this.html.used&&this._html_setProperty("muted",a);this.flash.used&&this._flash_mute(a);this.html.video.gate||this.html.audio.gate||(this._updateMute(a),this._updateVolume(this.options.volume),this._trigger(b.jPlayer.event.volumechange))},mute:function(a){a=a===f?!0:!!a;this._muted(a)},unmute:function(a){a=a===f?!0:!!a;this._muted(!a)},_updateMute:function(a){a===f&&(a=this.options.muted);this.css.jq.mute.length&&this.css.jq.unmute.length&&(this.status.noVolume? +(this.css.jq.mute.hide(),this.css.jq.unmute.hide()):a?(this.css.jq.mute.hide(),this.css.jq.unmute.show()):(this.css.jq.mute.show(),this.css.jq.unmute.hide()))},volume:function(a){this.volumeWorker(a);this.options.globalVolume&&this.tellOthers("volumeWorker",function(){return this.options.globalVolume},a)},volumeWorker:function(a){a=this._limitValue(a,0,1);this.options.volume=a;this.html.used&&this._html_setProperty("volume",a);this.flash.used&&this._flash_volume(a);this.html.video.gate||this.html.audio.gate|| +(this._updateVolume(a),this._trigger(b.jPlayer.event.volumechange))},volumeBar:function(a){if(this.css.jq.volumeBar.length){var c=b(a.currentTarget),d=c.offset(),e=a.pageX-d.left,g=c.width();a=c.height()-a.pageY+d.top;c=c.height();this.options.verticalVolume?this.volume(a/c):this.volume(e/g)}this.options.muted&&this._muted(!1)},volumeBarValue:function(){},_updateVolume:function(a){a===f&&(a=this.options.volume);a=this.options.muted?0:a;this.status.noVolume?(this.css.jq.volumeBar.length&&this.css.jq.volumeBar.hide(), +this.css.jq.volumeBarValue.length&&this.css.jq.volumeBarValue.hide(),this.css.jq.volumeMax.length&&this.css.jq.volumeMax.hide()):(this.css.jq.volumeBar.length&&this.css.jq.volumeBar.show(),this.css.jq.volumeBarValue.length&&(this.css.jq.volumeBarValue.show(),this.css.jq.volumeBarValue[this.options.verticalVolume?"height":"width"](100*a+"%")),this.css.jq.volumeMax.length&&this.css.jq.volumeMax.show())},volumeMax:function(){this.volume(1);this.options.muted&&this._muted(!1)},_cssSelectorAncestor:function(a){var c= +this;this.options.cssSelectorAncestor=a;this._removeUiClass();this.ancestorJq=a?b(a):[];a&&1!==this.ancestorJq.length&&this._warning({type:b.jPlayer.warning.CSS_SELECTOR_COUNT,context:a,message:b.jPlayer.warningMsg.CSS_SELECTOR_COUNT+this.ancestorJq.length+" found for cssSelectorAncestor.",hint:b.jPlayer.warningHint.CSS_SELECTOR_COUNT});this._addUiClass();b.each(this.options.cssSelector,function(a,b){c._cssSelector(a,b)});this._updateInterface();this._updateButtons();this._updateAutohide();this._updateVolume(); +this._updateMute()},_cssSelector:function(a,c){var d=this;"string"===typeof c?b.jPlayer.prototype.options.cssSelector[a]?(this.css.jq[a]&&this.css.jq[a].length&&this.css.jq[a].unbind(".jPlayer"),this.options.cssSelector[a]=c,this.css.cs[a]=this.options.cssSelectorAncestor+" "+c,this.css.jq[a]=c?b(this.css.cs[a]):[],this.css.jq[a].length&&this.css.jq[a].bind("click.jPlayer",function(c){c.preventDefault();d[a](c);b(this).blur()}),c&&1!==this.css.jq[a].length&&this._warning({type:b.jPlayer.warning.CSS_SELECTOR_COUNT, +context:this.css.cs[a],message:b.jPlayer.warningMsg.CSS_SELECTOR_COUNT+this.css.jq[a].length+" found for "+a+" method.",hint:b.jPlayer.warningHint.CSS_SELECTOR_COUNT})):this._warning({type:b.jPlayer.warning.CSS_SELECTOR_METHOD,context:a,message:b.jPlayer.warningMsg.CSS_SELECTOR_METHOD,hint:b.jPlayer.warningHint.CSS_SELECTOR_METHOD}):this._warning({type:b.jPlayer.warning.CSS_SELECTOR_STRING,context:c,message:b.jPlayer.warningMsg.CSS_SELECTOR_STRING,hint:b.jPlayer.warningHint.CSS_SELECTOR_STRING})}, +seekBar:function(a){if(this.css.jq.seekBar.length){var c=b(a.currentTarget),d=c.offset();a=a.pageX-d.left;c=c.width();this.playHead(100*a/c)}},playBar:function(){},playbackRate:function(a){this._setOption("playbackRate",a)},playbackRateBar:function(a){if(this.css.jq.playbackRateBar.length){var c=b(a.currentTarget),d=c.offset(),e=a.pageX-d.left,g=c.width();a=c.height()-a.pageY+d.top;c=c.height();this.playbackRate((this.options.verticalPlaybackRate?a/c:e/g)*(this.options.maxPlaybackRate-this.options.minPlaybackRate)+ +this.options.minPlaybackRate)}},playbackRateBarValue:function(){},_updatePlaybackRate:function(){var a=(this.options.playbackRate-this.options.minPlaybackRate)/(this.options.maxPlaybackRate-this.options.minPlaybackRate);this.status.playbackRateEnabled?(this.css.jq.playbackRateBar.length&&this.css.jq.playbackRateBar.show(),this.css.jq.playbackRateBarValue.length&&(this.css.jq.playbackRateBarValue.show(),this.css.jq.playbackRateBarValue[this.options.verticalPlaybackRate?"height":"width"](100*a+"%"))): +(this.css.jq.playbackRateBar.length&&this.css.jq.playbackRateBar.hide(),this.css.jq.playbackRateBarValue.length&&this.css.jq.playbackRateBarValue.hide())},repeat:function(){this._loop(!0)},repeatOff:function(){this._loop(!1)},_loop:function(a){this.options.loop!==a&&(this.options.loop=a,this._updateButtons(),this._trigger(b.jPlayer.event.repeat))},currentTime:function(){},duration:function(){},gui:function(){},noSolution:function(){},option:function(a,c){var d=a;if(0===arguments.length)return b.extend(!0, +{},this.options);if("string"===typeof a){var e=a.split(".");if(c===f){for(var d=b.extend(!0,{},this.options),g=0;g= +a&&(b=!0);return b},_validString:function(a){return a&&"string"===typeof a},_limitValue:function(a,b,d){return ad?d:a},_urlNotSetError:function(a){this._error({type:b.jPlayer.error.URL_NOT_SET,context:a,message:b.jPlayer.errorMsg.URL_NOT_SET,hint:b.jPlayer.errorHint.URL_NOT_SET})},_flashError:function(a){var c;c=this.internal.ready?"FLASH_DISABLED":"FLASH";this._error({type:b.jPlayer.error[c],context:this.internal.flash.swf,message:b.jPlayer.errorMsg[c]+a.message,hint:b.jPlayer.errorHint[c]}); +this.internal.flash.jq.css({width:"1px",height:"1px"})},_error:function(a){this._trigger(b.jPlayer.event.error,a);this.options.errorAlerts&&this._alert("Error!"+(a.message?"\n"+a.message:"")+(a.hint?"\n"+a.hint:"")+"\nContext: "+a.context)},_warning:function(a){this._trigger(b.jPlayer.event.warning,f,a);this.options.warningAlerts&&this._alert("Warning!"+(a.message?"\n"+a.message:"")+(a.hint?"\n"+a.hint:"")+"\nContext: "+a.context)},_alert:function(a){a="jPlayer "+this.version.script+" : id='"+this.internal.self.id+ +"' : "+a;this.options.consoleAlerts?console&&console.log&&console.log(a):alert(a)},_emulateHtmlBridge:function(){var a=this;b.each(b.jPlayer.emulateMethods.split(/\s+/g),function(b,d){a.internal.domNode[d]=function(b){a[d](b)}});b.each(b.jPlayer.event,function(c,d){var e=!0;b.each(b.jPlayer.reservedEvent.split(/\s+/g),function(a,b){if(b===c)return e=!1});e&&a.element.bind(d+".jPlayer.jPlayerHtml",function(){a._emulateHtmlUpdate();var b=document.createEvent("Event");b.initEvent(c,!1,!0);a.internal.domNode.dispatchEvent(b)})})}, +_emulateHtmlUpdate:function(){var a=this;b.each(b.jPlayer.emulateStatus.split(/\s+/g),function(b,d){a.internal.domNode[d]=a.status[d]});b.each(b.jPlayer.emulateOptions.split(/\s+/g),function(b,d){a.internal.domNode[d]=a.options[d]})},_destroyHtmlBridge:function(){var a=this;this.element.unbind(".jPlayerHtml");b.each((b.jPlayer.emulateMethods+" "+b.jPlayer.emulateStatus+" "+b.jPlayer.emulateOptions).split(/\s+/g),function(b,d){delete a.internal.domNode[d]})}};b.jPlayer.error={FLASH:"e_flash",FLASH_DISABLED:"e_flash_disabled", +NO_SOLUTION:"e_no_solution",NO_SUPPORT:"e_no_support",URL:"e_url",URL_NOT_SET:"e_url_not_set",VERSION:"e_version"};b.jPlayer.errorMsg={FLASH:"jPlayer's Flash fallback is not configured correctly, or a command was issued before the jPlayer Ready event. Details: ",FLASH_DISABLED:"jPlayer's Flash fallback has been disabled by the browser due to the CSS rules you have used. Details: ",NO_SOLUTION:"No solution can be found by jPlayer in this browser. Neither HTML nor Flash can be used.",NO_SUPPORT:"It is not possible to play any media format provided in setMedia() on this browser using your current options.", +URL:"Media URL could not be loaded.",URL_NOT_SET:"Attempt to issue media playback commands, while no media url is set.",VERSION:"jPlayer "+b.jPlayer.prototype.version.script+" needs Jplayer.swf version "+b.jPlayer.prototype.version.needFlash+" but found "};b.jPlayer.errorHint={FLASH:"Check your swfPath option and that Jplayer.swf is there.",FLASH_DISABLED:"Check that you have not display:none; the jPlayer entity or any ancestor.",NO_SOLUTION:"Review the jPlayer options: support and supplied.",NO_SUPPORT:"Video or audio formats defined in the supplied option are missing.", +URL:"Check media URL is valid.",URL_NOT_SET:"Use setMedia() to set the media URL.",VERSION:"Update jPlayer files."};b.jPlayer.warning={CSS_SELECTOR_COUNT:"e_css_selector_count",CSS_SELECTOR_METHOD:"e_css_selector_method",CSS_SELECTOR_STRING:"e_css_selector_string",OPTION_KEY:"e_option_key"};b.jPlayer.warningMsg={CSS_SELECTOR_COUNT:"The number of css selectors found did not equal one: ",CSS_SELECTOR_METHOD:"The methodName given in jPlayer('cssSelector') is not a valid jPlayer method.",CSS_SELECTOR_STRING:"The methodCssSelector given in jPlayer('cssSelector') is not a String or is empty.", +OPTION_KEY:"The option requested in jPlayer('option') is undefined."};b.jPlayer.warningHint={CSS_SELECTOR_COUNT:"Check your css selector and the ancestor.",CSS_SELECTOR_METHOD:"Check your method name.",CSS_SELECTOR_STRING:"Check your css selector is a string.",OPTION_KEY:"Check your option name."}}); \ No newline at end of file diff --git a/video/jplayer/skins/premium/gui.php b/video/jplayer/skins/premium/gui.php new file mode 100644 index 000000000..6aa8037db --- /dev/null +++ b/video/jplayer/skins/premium/gui.php @@ -0,0 +1,32 @@ + \ No newline at end of file diff --git a/video/jplayer/skins/premium/jplayer.premium.css b/video/jplayer/skins/premium/jplayer.premium.css new file mode 100755 index 000000000..a4aba28f7 --- /dev/null +++ b/video/jplayer/skins/premium/jplayer.premium.css @@ -0,0 +1,188 @@ +/* @override + http://wpdev.dynalias.com/focus/wp-content/themes/focus/jplayer/skins/siteorigin/jplayer.siteorigin.css?ver=3.5 */ + +.jp-audio, +.jp-audio-stream, +.jp-video { + background-color:#1c1c1c; + position: relative; +} + +.jp-video .jp-type-single{ +} + +/* @group GUI */ + +.jp-video .jp-gui{ + height: 35px; + overflow: hidden; + margin-top: -35px; + + background-image: -webkit-gradient(linear, left top, left bottom, from(#F1F1F1), to(#D1D1D1)); + + box-shadow: 0 0 1px rgba(255,255,255,1); + + position: relative; +} + +.jp-video .jp-gui .jp-controls { + list-style: none; + display: block; + margin: 0; +} + +.jp-video .jp-gui .jp-controls li a{ + position: absolute; + display: block; + text-indent: -9999px; + overflow: hidden; +} + +.jp-video .jp-gui .jp-controls .jp-play, +.jp-video .jp-gui .jp-controls .jp-pause{ + width: 15px; + height: 15px; + overflow: hidden; + + top: 11px; + left: 15px; + + background: url('sprites/play.png'); +} + +.jp-video .jp-gui .jp-controls .jp-pause{ + background: url('sprites/pause.png'); + width: 13px; +} + +/* @end */ + +/* @group Progress Bar */ + +.jp-video .jp-progress{ + display: block; + height: 10px; + width: auto; + margin: 0 125px 0 51px; + background: #706d6d; + margin-top: 13px; + + border-radius: 10px; + box-shadow: inset 0 1px 4px rgba(0,0,0,0.4), 0 1px 0 #FFF; +} + +.jp-video .jp-progress .jp-seek-bar{ + height: 10px; + background: rgba(34,34,34,0.34); + border-radius: 10px; + + cursor: pointer; +} + +.jp-video .jp-progress .jp-play-bar{ + position: relative; + height: 10px; + background: #bb2a1e; + border-radius: 10px; + box-shadow: inset 0 1px 4px rgba(0,0,0,0.4); +} + +.jp-video .jp-progress .jp-play-bar-marker{ + position: absolute; + width: 16px; + height: 17px; + background: url('sprites/handle.png') no-repeat; + right: -8px; + top: -3px; +} + +/* @end */ + +.jp-video .jp-time-info{ + + position: absolute; + right: 34px; + top: 50%; + margin-top: -0.475em; + width: 80px; + color: #444; + text-shadow: 0 1px 0 #FFF; + font: 11px/1 Arial, sans-serif; +} + +.jp-video .jp-time-info > div{ + display: inline; +} + +.jp-video .jp-full-screen, +.jp-video .jp-restore-screen{ + display: block; + top: 11px; + right: 15px; + + width: 14px; + height: 15px; + background: url('sprites/full-screen.png') no-repeat; +} + +/* @group Full Screen */ + +.jp-video-full .jp-jplayer{ + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 100000; /* One higher than the WordPress admin bar */ + background: #000000; +} + +.jp-video-full .jp-gui{ + position: fixed; + left: 0; + bottom: 0; + width: 100%; + z-index: 100001; /* One higher than the video player */ +} + +/* @end */ + +.jp-video .sep{ + position: absolute; + display: block; + width: 2px; + height: 21px; + background: url('sprites/sep.png') no-repeat; + top: 7px; +} + +.jp-video .jp-controls-sep{ + left: 40px; +} + +.jp-video .jp-time-sep{ + right: 235px; +} + +.jp-video .jp-full-sep{ + right: 39px; +} + +.jp-video .jp-play{ + position: absolute; + top: 50%; + left: 50%; + cursor: pointer; +} + +.jp-video .jp-play-default{ + margin-top: -49px; + margin-left: -49px; + + width: 97px; + height: 97px; +} + +.jp-video .jp-gui{ + display: none; +} \ No newline at end of file diff --git a/video/jplayer/skins/premium/sprites/full-screen.png b/video/jplayer/skins/premium/sprites/full-screen.png new file mode 100644 index 0000000000000000000000000000000000000000..2dcdfe75b68369212ad37a4363381f88e131c086 GIT binary patch literal 1216 zcmeAS@N?(olHy`uVBq!ia0vp^d_c_4!3HF+i2N%7Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS=07?{&CLn2Bde0{8v^Ko2Tt~skz|cV7z)0WFNY~KZ%Gk)tz(4^Clz_GsrKDK}xwt{?0`hE?GD=Dctn~HE z%ggo3jrH=2()A53EiFN27#ZmTRp=I1=9MH?=;jqG!%T2VElw`VEGWs$&r<-In3$Ab zT4JjNbScCOxdpzyaD(%Tp#cR9GX09g0)0b01O41wkiWpHi%Wu15zfG>x;Uh=AXPso zwK%`DC>aHCFybnZQU%%yG$2F3nBNEAe!(RRYTBrDUd9xfnUQ zn3$Wmm^d35m{}MaT9_NUIUAU}IGejUI~zJ$z|6p=*TmJrz{$+jz{JJH#L&>y$jQmv z+!@HSba69uHLx^<>GjMjE=kNwPKDW<3A7iY*ToI5UMuIK)WnkfqLBRj99Rkn$jC3r zFV4s>P;d@5QSePn&ddYxLD2*8txIZAW?5>ATTy=Gku_A^g)RODY3wWfGH5fgeQF<2cCIS^ME;~2$(f&6&@`DW~&dLE{-7;x8?-B z-?Y(49PFZK7-9w?A1Te4@uzUD(Lns3T?Yq$o5O|UWTT@&!|rg1&{ zeAS+kY0V9Z7Aeu87hgQzHrI6L7uR&v(@%}Q?bT;wV{laLUNYIh?;)s6@pScbS?83{ F1ORqplt%yn literal 0 HcmV?d00001 diff --git a/video/jplayer/skins/premium/sprites/handle.png b/video/jplayer/skins/premium/sprites/handle.png new file mode 100644 index 0000000000000000000000000000000000000000..c49647f2a565aa881eec8eebf64f4d95c52f98ab GIT binary patch literal 1635 zcmbVMeNfY87_Z<5I6&r!4ww?lHb0^#yI>19B1 zn}H^*3_+2AhY{0Ld5nz9dO9A0#j4S~BqnVWiDFYGuwzyM#0LcyizluzZ5yM*|8e7$ z+P0KJD=yICHY$%cu=U7}^n%&kz21;Vk@W^yMH6gMuw0Zf?bHul}MyiD24wO_c+%0e{v>Zoe_AN<6q6FoK7^mpOmXDrWn}#8t?7 z>VfHS_nAtv$;BybXl!hB*lf1E;^Nr4n-4dyIr_yp&;1O?E=flg7+lqL>UdMrvm%GX z(Q<9rui?_!R->?sBuV$7+S>e#qpRn-<3;xu^gS8*t-rs2cNX*Xx#N#Ro6z9fYcev5 z_jU}r%d;lEII~N2xW4jEIej|1qN3t+;$UZ2SNrJb=*4Y2oJk9hq^@3lH{DtrxK&=3 z-@_mQ8)!Umdd0PwK*z)l;vT53I*6rxV1vC8#QJB1Ma1dYg z>59l$2XA(VZ|kk}gosN)4X!ulM(_lCd6F7!o_O(*Aot2+Pp&BUM}^&AHnK5JDwBoO z-;A2KdwE0eH&;XRSGcZ!-FF?EvSeTN1*LN5`vFro@97>IeEc}6W*xtxRS!Y$ZL#20 z>15r3kV7>!!|ldjL)+?ZEh|~v-_x^iTCPN#GE#XkMJlN*KQ zAz6Rk9A=6N%;twtdiNwvU@V_(szNjGZFpzq0hvT%|L8*5;hu%A(=9DgYuBzF5R1ie zb3VCXu5Vp$DFSTr7Ck1xo literal 0 HcmV?d00001 diff --git a/video/jplayer/skins/premium/sprites/large-play.png b/video/jplayer/skins/premium/sprites/large-play.png new file mode 100644 index 0000000000000000000000000000000000000000..81ecaeb47db4dc65f15b9ca57846b2e8d36238ad GIT binary patch literal 4492 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%0AayD7X|ndzlw|&TI#-qacnsYdo~(Yg%^a`2 y{WZ2lm>r39pPynHAI;sI8jL6X;)gZKl^clzqx!??HxC{Mv!9k*rF$*MhkgSw%W%K| literal 0 HcmV?d00001 diff --git a/video/jplayer/skins/premium/sprites/play.png b/video/jplayer/skins/premium/sprites/play.png new file mode 100644 index 0000000000000000000000000000000000000000..fd8eed9aa47dcd418665ae7053fc3c02fc556f79 GIT binary patch literal 1365 zcmeAS@N?(olHy`uVBq!ia0vp^{2c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%s|1+P|wiV z#N6CmN5ROz&_Lh7NZ-&%*U;R`*vQJjKmiJrfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8EkR}&8R-I5=oVMzl_XZ^<`pZ$OmImpPA){ffi_eM3D1{oGuTzrd=COM+4n&cLd=IHa;5RX-@T zIKQ+g85kdF$}r8qu)}W=NFmTQR{lkqz(`5Vami0E%}vcK@pQ3O0?O#6WTsfT7&*C^ zn47qmI2#$5Sr{5xm>aq|8<@K|o4YwX8#-FR%)qAC&CS`#$iUUi#Kqaz(9qS$(aqS* z&BVma+|AP25GV}O>zP+vl9-pA3bQv8XfIT+DPFx+&PAz-CHX}m`T04p6cCV+Uy@&( zkzb(T9BiWCo0y!L2jYXG2jW|o)S}F?)D*X({9FZa_*!LRvER|v1?W^)XICR!{)XsH zAt%iAfsWA!B@U#-0uutJKoAq2^no0B=1I*1=9nU2*0}#&>OKPlW2>i&V@SoVDU;88 z9d-~nrhZb*w}nmrtKu8i4_BJ6%($ku%~x9IVED%;tADWlO?GtDy4u|I?TPCL1>GiB zeWePnn@4V%Xs%tdjjtx<=E?83=gw>~_Eu$lJn``keFlq{CA+6@NGfpe+x%|VyX6Ny zKWuydyIgMR@wI->t7gu;eqL<}rqe0tpQvED^IE zK5^`H^~e`&IUXg+$im{IxH6=Md)Z!&qPW#z+n=9ZQ^1xW#B$(Sqehpa%e;1pE**Zp z+}*jMTG3~Gp6xPU$=syS@UmBF!SQ(&#s^Dv&qQ)qsCFtlCl4tf$ew@4EpG&XA+FL*68QB<`j?DBHZ3wRe6?C4ielF{r G5}E*E?B&}4 literal 0 HcmV?d00001 diff --git a/video/jplayer/skins/premium/sprites/sep.png b/video/jplayer/skins/premium/sprites/sep.png new file mode 100644 index 0000000000000000000000000000000000000000..82c34e243e5765f4630d682155bc6243f1ba1dce GIT binary patch literal 1039 zcmbVL&ui0A91j+zY!2r^*hJxx9Xx20_tI=xv(=L3hpU)5x`I8LCa-H)lNXb>HQQ-; z5R`#eZ~g&-3{QfGc@n(%2M8WSkAip<5A&sMs)xCQf#m(j_w)IDe?DGcyEQ#|d6MI} z>H4bCVCxz7UOG3yzCBz$%a%E6w&(`w(7qF6PV-0`gL>d>;s$oS-3RaS3da>XezQee z_8rwFf#75|LK;L2&2cN$G;-W6OhFrO`eB9t{^p1WzE|PzE!fbGblmk<_hP)Ux7Kv` zw%oGES8sxqRAmGKrVdDhUYMw9g&**$Y@c0=JQ$eJtqMPMs%5VOox~U{2(akFMFwb<*Z4Z|8~u~UWbQW~kE*zfm+en}wlrijYr za>gM^MP^Y#oT62(#`rcNXx z0g6G8#Wk=_XaoOo<3#JExf@}zffKSFyR06a`5er0ce+q!$h=YS#y%^G(=&*>9pI4G zjSA2H2%hh$hEy|TMK)wzf{RNCEh)&-pVz)Nb$zBg!b+!NRXxTIC2^CGUfu!gT|!CHB@xg!WMDh4A7nQ~FojL#+2dITM*PV%8jgHA{ZQIxyRgTb~`q z!-GRMF)%-)yXW59>%{Aj??f%2xYBWF) literal 0 HcmV?d00001 diff --git a/video/jplayer/skins/siteorigin/gui.php b/video/jplayer/skins/siteorigin/gui.php new file mode 100644 index 000000000..acca2582a --- /dev/null +++ b/video/jplayer/skins/siteorigin/gui.php @@ -0,0 +1,33 @@ + \ No newline at end of file diff --git a/video/jplayer/skins/siteorigin/jplayer.siteorigin.css b/video/jplayer/skins/siteorigin/jplayer.siteorigin.css new file mode 100755 index 000000000..e92527dc6 --- /dev/null +++ b/video/jplayer/skins/siteorigin/jplayer.siteorigin.css @@ -0,0 +1,183 @@ +/* @override + http://wpdev.dynalias.com/focus/wp-content/themes/focus/jplayer/skins/siteorigin/jplayer.siteorigin.css?ver=3.5 */ + +.jp-audio, +.jp-audio-stream, +.jp-video { + position: relative; +} + +.jp-video .jp-type-single{ +} + +/* @group GUI */ + +.jp-video .jp-gui{ + height: 60px; + margin-top: -60px; + + position: relative; + overflow: hidden; + + opacity: 0.85; +} + +.jp-video .jp-gui .jp-controls { + list-style: none; + display: block; + margin: 0; +} + +.jp-video .jp-gui .jp-controls a{ + position: absolute; + display: block; + text-indent: -9999px; + overflow: hidden; +} + +.jp-video .jp-gui .jp-controls .jp-play, +.jp-video .jp-gui .jp-controls .jp-pause{ + width: 55px; + height: 35px; + overflow: hidden; + display:block; + + top: 10px; + left: 15px; + margin: 0 !important; + + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; +} + +.jp-video .jp-gui .jp-controls .jp-play{ + background: rgba(0,0,0,0.8) url('sprites/play.png') center center no-repeat; +} + +.jp-video .jp-gui .jp-controls .jp-pause{ + background: rgba(0,0,0,0.8) url('sprites/pause.png') center center no-repeat; +} + +/* @end */ + +/* @group Progress Bar */ + +.jp-video .jp-progress-wrapper{ + display: block; + width: auto; + margin: 0px 65px 0 90px; + padding-top: 10px; +} + +.jp-video .jp-progress{ + + background-color: rgba(0,0,0,0.8); + margin-top: 13px; + + border-radius: 3px; +} + +.jp-video .jp-progress .jp-seek-bar{ + height: 10px; + background: rgba(34,34,34,0.34); + border-radius: 3px; + + cursor: pointer; +} + +.jp-video .jp-progress .jp-play-bar{ + position: relative; + height: 10px; + background: #3fabe9; + border-radius: 3px; +} + +/* @end */ + +.jp-video .jp-time-info{ + + width: 40px; + padding: 3px 2px; + + position: absolute; + right: -21px; + bottom: 15px; + + background-color: rgba(0,0,0,0.8); + + color: #777777; + font: 11px/1 Arial, sans-serif; + text-align:center; + + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + + -moz-border-radius: 2px; + -webkit-border-radius: 2px; + border-radius: 2px; +} + +.jp-video .jp-time-info .jp-time-info-pointer{ + position: absolute; + width: 7px; + height: 4px; + left: 50%; + margin-left: -3.5px; + bottom: -4px; + background: url("sprites/time-pointer.png") no-repeat; +} + +.jp-video .jp-time-info > div{ + display: inline; +} + +.jp-video .jp-full-screen, +.jp-video .jp-restore-screen{ + display: block; + top: 19px; + right: 25px; + + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + + width: 18px; + height: 18px; + background: url('sprites/expand.png') center center no-repeat; + background-color: #222222; + background-color: rgba(0,0,0,0.8); +} + +.jp-video .jp-restore-screen{ + background: url('sprites/restore.png') no-repeat center center no-repeat; + background-color: #222222; + background-color: rgba(0,0,0,0.8); +} + +/* @group Full Screen */ + +.jp-video-full .jp-jplayer{ + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 100000; /* One higher than the WordPress admin bar */ + background: #000000; +} + +.jp-video-full .jp-gui{ + position: fixed; + left: 0; + bottom: 0; + width: 100%; + z-index: 100001; /* One higher than the video player */ +} + +/* @end */ + +.jp-video .jp-play-initial{ + display:none !important; +} \ No newline at end of file diff --git a/video/jplayer/skins/siteorigin/sprites/expand.png b/video/jplayer/skins/siteorigin/sprites/expand.png new file mode 100644 index 0000000000000000000000000000000000000000..7d48743f575c8b501df9cfba6b5792d26c01e919 GIT binary patch literal 1053 zcmaJ=TSyd97#_?*HZM?^SOmjpP}I(4cV~46S95n<-EhQq7ZeO_oH@FK&c&IdJ4!@Y zl7S{fZxxs#-Jpl?p$G{oEMKA^D(a!ImmsT$o}}$^W_7iPwqa(@IX~a`{nvA@wWYDh zwcCYZSW&czkD;{?eZ>Xa(0|Xu&lR+k!-jSk*Lz?}GyzsG>)im4s$wsQ0Z|?vSOPT| zwxv=@w8M6xnUi#t6dep{s|I3YSWTU6h*BSbcsJ-(v>@^A{sRJ6Atqz-i}sT+7W?#K5FqNHe!;XsBV2wYUNpr}b8$_ELgK`M&OMSS%k)*tdz z`$8^y*atgFsKId%mY&y0&>LERXme4 zr{s=>&Z)P~m2=0!tjlo5SjnM}hns*Zo_SebyBXlXIqI`YA zemj4ZDLzFPoOygzJHPiw{*zz7x7NczzN^lLj%aQ9-@@?6`8_e~3<%~#5&PRu-;=;%CsByVtt9ACYD;qcgE*G!t7 zUo9=$@#*Abxa6g}_vCQpCSSX8r)*bIsdHhY4K4ib H(DBhfi!fZl literal 0 HcmV?d00001 diff --git a/video/jplayer/skins/siteorigin/sprites/large-play.png b/video/jplayer/skins/siteorigin/sprites/large-play.png new file mode 100644 index 0000000000000000000000000000000000000000..81ecaeb47db4dc65f15b9ca57846b2e8d36238ad GIT binary patch literal 4492 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 W(G%QPmoj#P3L#HdKbLh*2~7abcDEG( literal 0 HcmV?d00001 diff --git a/video/jplayer/skins/siteorigin/sprites/restore.png b/video/jplayer/skins/siteorigin/sprites/restore.png new file mode 100644 index 0000000000000000000000000000000000000000..9257c9d9868e2bdbd98e24ac50fddc9f88904048 GIT binary patch literal 1068 zcmaJ>PfXKL9Ij$eks(G!0YjQ%f*7~1+gey*B3qf8+6iNZ1vRm2AFPi4p?$c5Xq3c* ziHasBUQnWU33@UihzAp5f_l*eFeXM%CMN2|Xq4zHF!11P+P?RG`M&S>x4qmNJLo8? zD8ewz5sh$hv=*bMWJ4i(S57W`LQ6SpNy2um3uXlaV9k=20(evrx9L zyX8a@Cix?*s3|VN#<(m+M{EphY_fDg%m9d|K)0+0h|d#K1TISfqJ!q$ydDB+IWk~? z_JLSJ9LR`%iD=r7H(D$bPyiHgOX*Qf)(Q}dx-8n;W|F`cAutmlmV!$1t$0W?08YES zPSH(KIK#LoAI&h-KHTG`Jfs`ljFa-QG{bu8@a2m@-VCXWjdS5;U+5Gd(h%w_NoKQI zSJvy&jBb+h`~9|t$Kyl@r`fMU!E&l*bxwf;rfA4IlrIIsF`U^$3yK5zK0h?S+xzdiwvG;qpAr7RRmEkKp+j5EK6+I*BqpoAXD!P z(iGL=srS}}f?l7S4*9rHy*r%aa+=tu02SuA(kizuFV`LhMMs`FV8~~H6gD&kUrd^n z^T*=LtGCRR^2b8w<&r2E(r)cvtgJTlZq2>3aChp#S>|SONBN V=IkgxH*EhGuxLw+8w;Kq`UBKtS&RSx literal 0 HcmV?d00001 diff --git a/video/jplayer/skins/siteorigin/sprites/time-pointer.png b/video/jplayer/skins/siteorigin/sprites/time-pointer.png new file mode 100644 index 0000000000000000000000000000000000000000..3a54d35b7ec921f38e4042fe84f99c4de403c12d GIT binary patch literal 965 zcmaJ=J#W)M7(UUeMU{$RYne_iNJS$4itQv;O^NNKwNzJ85{Znx*q6j=?KAd;xRqGg zn2;EdKouJ+VrKv(gv8E-#L_>&A7J2|)G0%YW&7@4z0dPL@5eoF?eDCv++G0ytU9}P zo2|?2TfDx+{^qy4Pua3g8$H?~13Ezo26dnGF?2#S#BGfHkDoXg_y+e#bwxL}@F>_lNpR<6zw4bGdNrTh zegL;Joe_kXBAA6oQL1MKH|5pYJ~s;-oVw6MgPV!!xh-gs1VfcCmpoCDpr-MXqH3CS zAIhR63nF{9lBDRWrppz0{%|Z>;t%w;-8_%QP6jukG}Z-SGMVs`GEb7BAk}KMoI{pN z%%hYZM-*kHDBUO+Y@B*Y5YvD}FlR)4GNuN{l+IEJ<2h}Vo|lOgOvq3yNW7S*R5-fs z|6Rjy?w!&$zR-IqaoRnOvCzgT87CfV++ZUQ73)@l5hY2NkfWlCtr4Ll9g!GX9Sv@} z$P1#}aBqrmUEPUNiXsm?w!twDJ_vkP&!*X^C~8%)ElFy~)pEr$%ZjL4%up4Z1=c3s zIK&Yxu>J*ByAqoZLm0EjHco;k*l#8zgwv$;;1VrOrEaR4S*|1}%Ua(71aqN0G``Pyo4mQEr+S1;K)Ailc zYyB~NwezyN_|+9}EZ-EsS-JIQ;n!LnzFmCx1;EeX^~0r8QC5EB7t3kv+waYTXMX_@ CStj!U literal 0 HcmV?d00001 diff --git a/video/panels.video.jquery.js b/video/panels.video.jquery.js new file mode 100644 index 000000000..f1e139b0c --- /dev/null +++ b/video/panels.video.jquery.js @@ -0,0 +1,64 @@ +jQuery(function($){ + + $(window ).resize(function(){ + $('.jp-jplayer' ).each(function(){ + var $$ = $(this); + + if($$.data('player-ready') != undefined){ + // Change the height of the player + var ratio = Number($$.attr('data-ratio')); + $$.jPlayer( { size: {height: Math.floor($$.closest('.widget' ).outerWidth() / 1.777)} } ); + } + }); + }) + + + $('.jp-jplayer' ).each(function(){ + var $$ = $(this); + + var ratio = Number($$.attr('data-ratio')); + + $$.jPlayer({ + ready: function(){ + $$.data('player-ready', true); + + $(this).jPlayer("setMedia", { + m4v : $(this).attr('data-video'), + poster: $(this).attr('data-poster') + } ); + + if($(this ).attr('data-mobile') == 'true'){ + $(this).find('.jp-gui' ).hide(); + } + else{ + $(this ).find('.jp-gui' ).show(); + } + + // Check if we're using autoplay + if(Number($(this).attr('data-autoplay')) == 1){ $$.jPlayer("play"); } + + $(this).jPlayer( { size: {height: Math.floor($(this).closest('.widget' ).outerWidth() / ratio)} } ); + }, + solution: "flash, html", + supplied : "m4v", + swfPath : $(this).attr('data-swfpath'), + autohide : { + restored: false, + full: false + }, + play: function(){ + $(this).jPlayer("pauseOthers"); + $(this ).jPlayer('option', 'autohide', { + restored: true, + full: true, + hold: 2000 + }); + }, + size: { + width: "100%", + height: Math.floor($$.closest('.widget' ).outerWidth() / ratio) + }, + cssSelectorAncestor: "#" + $$.closest('.jp-video' ).attr('id') + }); + }); +}); \ No newline at end of file diff --git a/video/poster.jpg b/video/poster.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a50df523d198ced854380b60a0d202e499a3551c GIT binary patch literal 59406 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 literal 0 HcmV?d00001 diff --git a/widgets/basic.php b/widgets/basic.php new file mode 100644 index 000000000..d76ffafc1 --- /dev/null +++ b/widgets/basic.php @@ -0,0 +1,439 @@ + __( 'A full SiteOrigin Page Builder layout as a widget.', 'siteorigin-panels' ), + 'panels_title' => false, + ), + array( + ) + ); + } + + function widget($args, $instance) { + if( empty($instance['panels_data']) ) return; + + if( is_string( $instance['panels_data'] ) ) + $instance['panels_data'] = json_decode( $instance['panels_data'], true ); + if(empty($instance['panels_data']['widgets'])) return; + + if( empty( $instance['builder_id'] ) ) $instance['builder_id'] = uniqid(); + + echo $args['before_widget']; + echo siteorigin_panels_render( 'w'.$instance['builder_id'], true, $instance['panels_data'] ); + echo $args['after_widget']; + } + + function update($new, $old) { + $new['builder_id'] = uniqid(); + return $new; + } + + function form($instance){ + $instance = wp_parse_args($instance, array( + 'panels_data' => '', + 'builder_id' => uniqid(), + ) ); + + if( !is_string( $instance['panels_data'] ) ) $instance['panels_data'] = json_encode( $instance['panels_data'] ); + + ?> +
+

+ + + +
+ + __( 'Displays some form of post content form the current post.', 'siteorigin-panels' ), + ) + ); + } + + function widget( $args, $instance ) { + if( is_admin() ) return; + + echo $args['before_widget']; + $content = apply_filters('siteorigin_panels_widget_post_content', $this->default_content($instance['type'])); + echo $content; + echo $args['after_widget']; + } + + /** + * The default content for post types + * @param $type + * @return string + */ + function default_content($type){ + global $post; + if(empty($post)) return; + + switch($type) { + case 'title' : + return '

' . $post->post_title . '

'; + case 'content' : + return '
' . wpautop($post->post_content) . '
'; + case 'featured' : + if(!has_post_thumbnail()) return ''; + return ''; + default : + return ''; + } + } + + function update($new, $old){ + return $new; + } + + function form( $instance ) { + $instance = wp_parse_args($instance, array( + 'type' => 'content', + )); + + $types = apply_filters('siteorigin_panels_widget_post_content_types', array( + '' => __('None', 'siteorigin-panels'), + 'title' => __('Title', 'siteorigin-panels'), + 'featured' => __('Featured Image', 'siteorigin-panels'), + )); + + ?> +

+ + +

+ __( 'Displays a post loop.', 'siteorigin-panels' ), + ) + ); + } + + /** + * @param array $args + * @param array $instance + */ + function widget( $args, $instance ) { + if( empty( $instance['template'] ) ) return; + if( is_admin() ) return; + + $template = $instance['template']; + $query_args = $instance; + unset($query_args['template']); + unset($query_args['additional']); + unset($query_args['sticky']); + unset($query_args['title']); + + $query_args = wp_parse_args($instance['additional'], $query_args); + + global $wp_rewrite; + + if( $wp_rewrite->using_permalinks() ) { + + if( get_query_var('paged') ) { + // When the widget appears on a sub page. + $query_args['paged'] = get_query_var('paged'); + } + elseif( strpos( $_SERVER['REQUEST_URI'], '/page/' ) !== false ) { + // When the widget appears on the home page. + preg_match('/\/page\/([0-9]+)\//', $_SERVER['REQUEST_URI'], $matches); + if(!empty($matches[1])) $query_args['paged'] = intval($matches[1]); + else $query_args['paged'] = 1; + } + else $query_args['paged'] = 1; + } + else { + // Get current page number when we're not using permalinks + $query_args['paged'] = isset($_GET['paged']) ? intval($_GET['paged']) : 1; + } + + switch($instance['sticky']){ + case 'ignore' : + $query_args['ignore_sticky_posts'] = 1; + break; + case 'only' : + $query_args['post__in'] = get_option( 'sticky_posts' ); + break; + case 'exclude' : + $query_args['post__not_in'] = get_option( 'sticky_posts' ); + break; + } + + // Exclude the current post to prevent possible infinite loop + + global $siteorigin_panels_current_post; + + if( !empty($siteorigin_panels_current_post) ){ + if(!empty($query_args['post__not_in'])){ + $query_args['post__not_in'][] = $siteorigin_panels_current_post; + } + else { + $query_args['post__not_in'] = array( $siteorigin_panels_current_post ); + } + } + + if( !empty($query_args['post__in']) && !is_array($query_args['post__in']) ) { + $query_args['post__in'] = explode(',', $query_args['post__in']); + $query_args['post__in'] = array_map('intval', $query_args['post__in']); + } + + // Create the query + query_posts($query_args); + echo $args['before_widget']; + + // Filter the title + $instance['title'] = apply_filters('widget_title', $instance['title'], $instance, $this->id_base); + if ( !empty( $instance['title'] ) ) { + echo $args['before_title'] . $instance['title'] . $args['after_title']; + } + + add_filter( 'siteorigin_panels_filter_content_enabled', array( 'SiteOrigin_Panels_Widgets_PostLoop', 'remove_content_filter' ) ); + + global $more; $old_more = $more; $more = empty($instance['more']); + + if(strpos('/'.$instance['template'], '/content') !== false) { + while( have_posts() ) { + the_post(); + locate_template($instance['template'], true, false); + } + } + else { + locate_template($instance['template'], true, false); + } + + $more = $old_more; + remove_filter( 'siteorigin_panels_filter_content_enabled', array( 'SiteOrigin_Panels_Widgets_PostLoop', 'remove_content_filter' ) ); + + echo $args['after_widget']; + + // Reset everything + wp_reset_query(); + } + + /** + * @return bool + */ + static function remove_content_filter(){ + return false; + } + + /** + * Update the widget + * + * @param array $new + * @param array $old + * @return array + */ + function update($new, $old){ + $new['more'] = !empty( $new['more'] ); + return $new; + } + + /** + * Get all the existing files + * + * @return array + */ + function get_loop_templates(){ + $templates = array(); + + $template_files = array( + 'loop*.php', + '*/loop*.php', + 'content*.php', + '*/content*.php', + ); + + $template_dirs = array(get_template_directory(), get_stylesheet_directory()); + $template_dirs = array_unique($template_dirs); + foreach($template_dirs as $dir ){ + foreach($template_files as $template_file) { + foreach((array) glob($dir.'/'.$template_file) as $file) { + if( file_exists( $file ) ) $templates[] = str_replace($dir.'/', '', $file); + } + } + } + + $templates = array_unique($templates); + $templates = apply_filters('siteorigin_panels_postloop_templates', $templates); + sort($templates); + + return $templates; + } + + /** + * Display the form for the post loop. + * + * @param array $instance + * @return string|void + */ + function form( $instance ) { + $instance = wp_parse_args($instance, array( + 'title' => '', + 'template' => 'loop.php', + + // Query args + 'post_type' => 'post', + 'posts_per_page' => '', + + 'order' => 'DESC', + 'orderby' => 'date', + + 'sticky' => '', + + 'additional' => '', + 'more' => false, + )); + + $templates = $this->get_loop_templates(); + if( empty($templates) ) { + ?>

true)); + $post_types = array_values($post_types); + $post_types = array_diff($post_types, array('attachment', 'revision', 'nav_menu_item')); + + ?> +

+ + +

+

+ + +

+

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + />
+ +

+ +

+ + + query_posts.', 'siteorigin-panels'), 'http://codex.wordpress.org/Function_Reference/query_posts') ?> +

+ + TWHfz7|syHO(#s|gEt77!`qrPX`0!%I_kP^nP%3mV)4l|Ioslzi^-X7Cj&1X zc$wm=h@wsrChEgPA4ITt0YOCfG8m{2-iH^4;uHiC^<=Ko2Wv=j&iVPi@4qJd!p#k( zCG$%#3@a5HgAugOM$gO{)6siv-AFNXfyFDa}M|46CV4nUdTAARY&8s_rK~-nd2Js^TX$c}2Hq)`51l zvC9H2UCmLst3&oFMD1$4CdDHG4L}J`X$jrtQ+{GXmq&ZYOcD451a|m|-$BL1FkWX^ z0QZs}m+Yo#oa0EE^>Q3tg)?rNq1@=^Tr|sjIi9J;Cockdvy@go5)4iHLMK1b4x!0Y zR5F<)lOEEr+9=xR^Eny}<3b3Roz|h0a_M$OULgo<*-}lY8anPMN^zqT`Uw>2?-Vq1 zN>;Zg%Y+I>r6iN0Nw_chicjs+J+JEC*FSw+tIWMs0gr)PD@6OYprlxnS7lE zBxqPs!${<-7;ZP9VYeG5Uf06mOGQamb;q!5f+32$pxaQ=WgrCo1kxZ?RpB`=P5T&@ zTf?rbrs;Yn;PH4vOg+P~p_Po6kNZtBF{l!soQ}PvJ4HM zNSap*$HErWo8&5mWAPT`QYaY8Y3*OF&YRHuINPbgMTe=;2RfQ}3k~+Y(T#)Xqj)a_ z1JTrz-oBxdq7CIchxYaij4sZOjXa#Tv=;1nS)6!Xn%mWIIFT4D-$7K)6ytBoj*hon zUG!|^^gHh0@o)UsQ#)>+u-|Mw^eXcsUG;#OA-{cet$X-KS>K#YrSf7{&lw^TX__~X z`?fa`5;nWH%xdV1oGTvQ4>MVEp!kc_IJj0|FLw_Y?azHbcjCOS^5Tk<6>sk>%rPk`Bze}AES+r2eQ?dK0Y1KkZfQ7vkzZgw=vJB*n-vIIK&WpH4zAg<`xnt`h~EGJ literal 0 HcmV?d00001 diff --git a/widgets/img/checks/blue.png b/widgets/img/checks/blue.png new file mode 100644 index 0000000000000000000000000000000000000000..916dad17c87846cafb002a90ba22f4b79ff01146 GIT binary patch literal 1145 zcmaJ>O=#0l98b66v~Ht*ToiGStl(#pCT*gPtL@gTS}}8U1&1gsP2RR_CNCy$wwt2c zK&{|G6oiS2g7^s@ym(ODK@r4*g5p8+pr{}sqF($&U*6-k;y^_y0@|wzsXT z3oHy!6jhg9FXzZwPoCK`XOQ=%eK!hZS%6zRaR=(gMb&{+vyKW7WKFdP=Afz%?zjt^ zD5^SS~#Gn9s3@bsuzdlX_Lr>6~1tp@`Y1nJ5 zA97&FP+MLb>eu2rom>l=N)iz;Ayz@j+-|v2DM3%@N@VYuSsF}0aDRgS9aN{%4${bh zKwzR_Ey8g?6d5iih$6QV@DYw@BjgstTuc%~iC+aKFPeCB^lm99XC{4-Q-bcr*p^tf zSS&KdD1)3HmW#*Zo(9i{2_ozcT39WGEw{0vAVXJk3>zEB0-mB;Km#~IlSqH3VA@l% zmOEJ{QZTlp+APOJyp$?HMfpF}G^fxm&cQ$N{!`e^589B;K^F};8fjd2qvy(&(hgKH za`FgmuT-(U7h&Y~A{(STM6gs*HN)}@%O)6#B4sTXtCj|{a)KrrjA7_fG!u_U1R>fI zTfK_oTKVQ^RLJnHyco-@=7pG8;mSxGFrkGjT>TF>HZ9j12Gb^eanJ9uP-}KyF!`9_M^3jpVZvGl=@y(J~>u?5UM#_ zpAtrf{lQJ@$cC%Fiy?4o7QVALqEC4(#Iv4J_|TVtn>Kc+rlaH{TQv{@LL-ylE4 zzVf!={o~yy?6=>Bz!EC!_=4A}KUm;cdTz4j_LY?KKDFrZo9OW8HAhDM;O3!oZ)zW% zD@J_wg{qUU0y?4!%*y9(p!{t3_*#1Q=#T38{`kFy^7t7;+ON#p+L-^e7`!@g_RGxR uuEIlqaru@Taq40K)cg9rj;`3_tE#2^M{{>}&pF@a{S30LZSs|tuF+q?f^$*; literal 0 HcmV?d00001 diff --git a/widgets/img/checks/charcoal.png b/widgets/img/checks/charcoal.png new file mode 100644 index 0000000000000000000000000000000000000000..4b5e2413e0e31a7e5817cf5f8f27605cb6af08f9 GIT binary patch literal 1153 zcmaJ>TWAwm7#?d}wOWl=+(qy(4dN{`nMr2S4vk5hNle74Aq^VAhfZcrld;alnbRh% zNZWLK z|8?ekru{%e{de^QK{TXOVivFK@Uv#sH~4+{hZ_aFe2Wr!)M0j^vTQ@5RW%C`q;A{kWllZGQyBIJUufcLJMCcy#(bw|jL zLFJ_kh?_P992N8{0fqrQPcb2m=b3$g4KOSnz&Gz_LITGNY#1zFB=%;jT|!n&F8ac! z2w6hN5@@qM+IHCWb<5q_u7Kyt-zIE_^W5ju*kSx7St;3~=mvj;^; z9O=gtbZbf0a2Csi3r1IDi)N^Rn^F}hNneNR`V!hfS@ z=(@6mxD91w+BwraS*>EGWFpfknHGq5@L-oDE1KaNb}uj_Nk|(Gk_`o>#R!QtC{0rZ zF&qjf!eOy3)Wk7Nf^7{3xg?ul`B1WnZ0s-L)tnp}4nbPIbHp{?~o zHEEkVSV&sXYRA%6Q*V*0){Z4!lS|`ZXt%YWwYqA;^W$!p1{WWeMjsk@-fcYC+sFFH z@JI1FEw<(=4+?MJzmG0wI`98+EE_wq+gqHRn0VOfOEzYHTl>p{l`~gkZ|jCHjtt-P ztsGw$fK9@^lTXZdt_pKJT`itIJLoh`$FV& z?~l{dF9$B~Ok9!bQ==3yz4zXW@K3?w-Q(*YdqA2Z=6bBfRzKlB~ZuOkolkq;@Hvrz9oqV>9 zm#m*NcTNxfzT?*?jj`wFrA_}&m0ms}#`g?1d9FNLbM4KFRRN;qNXJ=ue%F-yQ%EP; K#cOSy-+utuG<{$I literal 0 HcmV?d00001 diff --git a/widgets/img/checks/green.png b/widgets/img/checks/green.png new file mode 100644 index 0000000000000000000000000000000000000000..8a5a60fe4f3333d02d1777c1af4e2aa049d30709 GIT binary patch literal 1144 zcmeAS@N?(olHy`uVBq!ia0vp^d_c_2!3HG#TlXXZDajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XRMoSU}&gdW~OIo zVrph)sH0$HU}&Uo07PcGh9*{~W>!Y#3Q(W~w5=#5%__*n4QdyVXRDM^Qc_^0uU}qX zu2*iXmtT~wZ)j<02{OaTNEfI=x41H|B(Xv_uUHvof=g;~a#3bMNoIbY0?5R~r2Ntn zTP2`NAzsKWfE$}v3=Jk=fazBx7U&!58GyV5Q|Rl9UukYGTy=3tP%6T`SPd=?sVqp< z4@xc0FD*(2MqHXQ$f^P>=c3falKi5O{QMkPC z!8&|>tvvIJOA_;vQ$1a5m4IgGWoD*W8JfA7Ihh(6IGI?O8ydPAIT{-IPQCg-$LNEi7AdM>LcsI`V!{(HkONQpsd>QkUIa|oep+(hfSKZzr;B4q z#jT!+XSo;yMH+fj9ttj*X!3rcMEAyx0oyhS^FQF-)8J%gR(8sNW8jh$#m<}~+!{+g zBKz`7UP>u1+jq|P`Mme%-o_j?@x5+;KOz2u@T>{OTRw&Tba&wra9ePYagn{NN~lNT zN}F#TzWR#Xrt1>Df-Yn=O;A~@_*N{4TPr}locmzopr E04?r-oB#j- literal 0 HcmV?d00001 diff --git a/widgets/img/checks/light.png b/widgets/img/checks/light.png new file mode 100644 index 0000000000000000000000000000000000000000..cd18bb04ecc34dc2aa85d913537e5d313afc4872 GIT binary patch literal 1153 zcmaJ>TWAwm7#?d}wQe^;6o=fr^#67;>>B2 zR+hF4>sEFbmfd}6Sw*ldRz#?;3W63jbH0qW-10 zf+|Q^&}P~Ya8%H*1Q-VJJjH}Ko@aIeHo&lS0N=cy2?-o8uwk%pk=UE9b_qE#wcrb% zVq_U1OQ7jWr9xGLlxde}CK`>p8Z7I_2*1;3Ai3%{oaUN>2pz@NETowRa24gE*@I#v zj`ULsy0s{4I16RM1*5C7MKe^uO{oTyq_0DDeG%=T9Q+*b7loaCp9Se0bj%)G!Hw%` zc3oLQn+;`T+IiDFTB~BVY$DSsn-*y6;K4RYRy4ykY@cUHl8`YRBpV9Mh%pjtP@1L+ zVmK5|hQrZ4p$Nw?Nwzf@{RX=lE>vG*;&@Jp)gtpcT z)s$`OU_NO5AeOUbP@nd``)A``Sp)SsbE}uT1_|PzXYGnAH zZ~55j1UEG3@$Q$0_Fh^t=>w-%q8kG~>22aj+`E$;=~+8`ePn!WEHV0kIJVY%`=8kH z-e0Gt-VB`Cnmi{prbj7aYUjPz;opL#yNB02^?(dT%=UtrrfEAp`~1x4%uocx?-H|f z>Be<~@7d9bVB%H7eNV%a>@wl@cQBEI<&o_84FX4U^H+p{Ak@Y^^H30rSG4XN} zFIm55?;Iced&{5CniH>1O6wm@mft)h#&!%wJm+4ny7+$CiU84apyPx*w{6n>DP)rE K;>A6kKm7;U#(jDK literal 0 HcmV?d00001 diff --git a/widgets/img/checks/orange.png b/widgets/img/checks/orange.png new file mode 100644 index 0000000000000000000000000000000000000000..53f5ba69a50d54b411d59cd9045dc6762ad47475 GIT binary patch literal 1139 zcmaJ>TWHi+7>*sS*4kP`(0YMmTDuB6lSyWh+SsvkNyiSDZk-M|Z)+wwZPPXvlT#<{ zgIGnV_+YJqQWZsHDt!>DE4Wlp7ll^#VHermB4v?^f~+75Dn1OP@Q&f~ZKe2uZwF;-`F7DSrQS;$8+X)u=g*+KmpBl`Kd^6e9ycLX$dS z5=u({p=sDi5T05!l}2fCA1@o4PjWFnM>8>-AR3z-Q<8fi0vXt;>LK#&qv6Ex?NLH5TGqvs)Ng>kgtpOV$+&SH zHLfd@k6BPchLtjm-a-}Ix(sC4U4{u_?Ht%4O0ufEhMfxxQREZ4jU-)$2_Zyc4WFti zJk7M{d?6p#bb>eb?W}8o<9?uKE8h01UZsB6@K$%`Cq@34vl&K?*&)N#>=jM z1f#j~@SBogo_%q@{LlXeFA=rVE5fstk0uAxm5Kb8sk*DVEU27%R~^o8O+-qnevUsy zrQJ_=Um$)zeKY)_6ukOv-JY?dGZi;p*W3a#P2tG!?fl@!&7(PaCiswzvTv6qkF<=Q vCMJI1M04!g2 literal 0 HcmV?d00001 diff --git a/widgets/img/checks/pink.png b/widgets/img/checks/pink.png new file mode 100644 index 0000000000000000000000000000000000000000..ee586e3b5bf813908233e4747c035e80724b9660 GIT binary patch literal 1139 zcmeAS@N?(olHy`uVBq!ia0vp^d_c_2!3HG#TlXXZDajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XRMoSU}&gdW~OIo zVrph)sH0$HU}&Uo07PcGh9*{~W>!Y#3Q(W~w5=#5%__*n4QdyVXRDM^Qc_^0uU}qX zu2*iXmtT~wZ)j<02{OaTNEfI=x41H|B(Xv_uUHvof=g;~a#3bMNoIbY0?5R~r2Ntn zTP2`NAzsKWfE$}v3=Jk=fazBx7U&!58GyV5Q|Rl9UukYGTy=3tP%6T`SPd=?sVqp< z4@xc0FD*(2MqHXQ$f^P>=c3falKi5O{QMkPC z!8&|>tvvIJOA_;vQ$1a5m4IgGWoD*W8JfA7Ihh(-IGI?O8ydPAIT{R#)nQG?l9x&XyNw)8*D^kO>0l#$ zBE2b#Nz<-l*-IvY-3+|-XMgP7lWsdfxItwnL&xpL3Uh(eikDSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XRMoSU}&gdW~OIo zVrph)sH0$HU}&Uo07PcGh9*{~W>!Y#3Q(W~w5=#5%__*n4QdyVXRDM^Qc_^0uU}qX zu2*iXmtT~wZ)j<02{OaTNEfI=x41H|B(Xv_uUHvof=g;~a#3bMNoIbY0?5R~r2Ntn zTP2`NAzsKWfE$}v3=Jk=fazBx7U&!58GyV5Q|Rl9UukYGTy=3tP%6T`SPd=?sVqp< z4@xc0FD*(2MqHXQ$f^P>=c3falKi5O{QMkPC z!8&|>tvvIJOA_;vQ$1a5m4IgGWoD*W8M|3p8km}zJDFIR8ydPAIT{F! zNq_$Tw`Vqd@N?PkAcrhXfpo85n)eb^+>`?*^GvwKeq@!yAEu7EoLMYclbW-F+=T9( zXPTPA9OUU>BmAQ0zsIa6j$I8Af}WRM75LJdvZ9!eTyCr|n{b}xDoap}fbV5ig)&B| zf3rT`JXZa>aSnsZNmhqDd`EUSM(}z5krCLJ|RAEqf_p=3CGkaJIT(iu|N! z$_{TjeTDC=%=q|>VTo3Q1DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XRMoSU}&gdW~OIo zVrph)sH0$HU}&Uo07PcGh9*{~W>!Y#3Q(W~w5=#5%__*n4QdyVXRDM^Qc_^0uU}qX zu2*iXmtT~wZ)j<02{OaTNEfI=x41H|B(Xv_uUHvof=g;~a#3bMNoIbY0?5R~r2Ntn zTP2`NAzsKWfE$}v3=Jk=fazBx7U&!58GyV5Q|Rl9UukYGTy=3tP%6T`SPd=?sVqp< z4@xc0FD*(2MqHXQ$f^P>=c3falKi5O{QMkPC z!8&|>tvvIJOA_;vQ$1a5m4IgGWoD*WnVA|IS{j)cIhk0P8ydPAIT{-Hq`Ot#xRapbrW`PtXTmM^BdZ+#Fm=qG*>ORIeF>j~X=4M6 zfPZ46*Cxi5Dhl&BOODm6_t3UUg8ey$NAEqj zgc^Pr&01#c@P_F!3)6!DR;$yZC+;yEiHSJ$Ttr|uqiL;+7)!fWfWQ--dWC;&%U(*D z`4;pBoULw{B0uSwvcsEBU*S6|Gd@0JSfbTn!Ls5d_qrdf90m-x-Uk~k2=QYA6$+lN KelF{r5}E*0VS0W5 literal 0 HcmV?d00001 diff --git a/widgets/img/checks/tirquoise.png b/widgets/img/checks/tirquoise.png new file mode 100644 index 0000000000000000000000000000000000000000..da4aff02310354f034645dd4060f57ea32da78b6 GIT binary patch literal 1140 zcmeAS@N?(olHy`uVBq!ia0vp^d_c_2!3HG#TlXXZDajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XRMoSU}&gdW~OIo zVrph)sH0$HU}&Uo07PcGh9*{~W>!Y#3Q(W~w5=#5%__*n4QdyVXRDM^Qc_^0uU}qX zu2*iXmtT~wZ)j<02{OaTNEfI=x41H|B(Xv_uUHvof=g;~a#3bMNoIbY0?5R~r2Ntn zTP2`NAzsKWfE$}v3=Jk=fazBx7U&!58GyV5Q|Rl9UukYGTy=3tP%6T`SPd=?sVqp< z4@xc0FD*(2MqHXQ$f^P>=c3falKi5O{QMkPC z!8&|>tvvIJOA_;vQ$1a5m4IgGWoD*W8M|3p8km|oIhk0P8ydPAIT{?O~Y zW#tBpGvs3%-|#w|X;rQ9W85emaM?Y@aYKDWlS2AW=8xtkzQ;FYHf6VD-D6(zXM%}5 yW0HT)tOtIs-RA5Hp^Qfq9em>pj_Nx!Ff#mH7_oDY+3rYCIpFE)=d#Wzp$Pzg!g?nF literal 0 HcmV?d00001 diff --git a/widgets/img/textures/dark-dashed.png b/widgets/img/textures/dark-dashed.png new file mode 100644 index 0000000000000000000000000000000000000000..2e2bf9cc03366ef028585891c0b0d43740440587 GIT binary patch literal 3120 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 literal 0 HcmV?d00001 diff --git a/widgets/img/textures/light-dashed.png b/widgets/img/textures/light-dashed.png new file mode 100644 index 0000000000000000000000000000000000000000..13dd97a746952b73f5fc767e740aaf1c684da7d4 GIT binary patch literal 4840 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 XS}aI&Nv|w_|L(~WX=lnnpyK}rk-5*d literal 0 HcmV?d00001 diff --git a/widgets/js/admin.js b/widgets/js/admin.js new file mode 100644 index 000000000..f880b1b9f --- /dev/null +++ b/widgets/js/admin.js @@ -0,0 +1,2 @@ +jQuery(function($){ +}); \ No newline at end of file diff --git a/widgets/js/embedded-video.js b/widgets/js/embedded-video.js new file mode 100644 index 000000000..7af9b588c --- /dev/null +++ b/widgets/js/embedded-video.js @@ -0,0 +1,3 @@ +jQuery(function($){ + $('.siteorigin-fitvids').fitVids(); +}); \ No newline at end of file diff --git a/widgets/js/jquery.fitvids.js b/widgets/js/jquery.fitvids.js new file mode 100755 index 000000000..b5ca5ac41 --- /dev/null +++ b/widgets/js/jquery.fitvids.js @@ -0,0 +1,88 @@ +/*global jQuery */ +/*jshint multistr:true browser:true */ +/*! +* FitVids 1.0 +* +* Copyright 2011, Chris Coyier - http://css-tricks.com + Dave Rupert - http://daverupert.com +* Credit to Thierry Koblentz - http://www.alistapart.com/articles/creating-intrinsic-ratios-for-video/ +* Released under the WTFPL license - http://sam.zoy.org/wtfpl/ +* +* Date: Thu Sept 01 18:00:00 2011 -0500 +*/ + +(function( $ ){ + + "use strict"; + + $.fn.fitVids = function( options ) { + var settings = { + customSelector: null + }; + + if(!document.getElementById('fit-vids-style')) { + + var div = document.createElement('div'), + ref = document.getElementsByTagName('base')[0] || document.getElementsByTagName('script')[0]; + + div.className = 'fit-vids-style'; + div.id = 'fit-vids-style'; + div.style.display = 'none'; + div.innerHTML = '­'; + + ref.parentNode.insertBefore(div,ref); + + } + + if ( options ) { + $.extend( settings, options ); + } + + return this.each(function(){ + var selectors = [ + "iframe[src*='player.vimeo.com']", + "iframe[src*='youtube.com']", + "iframe[src*='youtube-nocookie.com']", + "iframe[src*='kickstarter.com'][src*='video.html']", + "object", + "embed" + ]; + + if (settings.customSelector) { + selectors.push(settings.customSelector); + } + + var $allVideos = $(this).find(selectors.join(',')); + $allVideos = $allVideos.not("object object"); // SwfObj conflict patch + + $allVideos.each(function(){ + var $this = $(this); + if (this.tagName.toLowerCase() === 'embed' && $this.parent('object').length || $this.parent('.fluid-width-video-wrapper').length) { return; } + var height = ( this.tagName.toLowerCase() === 'object' || ($this.attr('height') && !isNaN(parseInt($this.attr('height'), 10))) ) ? parseInt($this.attr('height'), 10) : $this.height(), + width = !isNaN(parseInt($this.attr('width'), 10)) ? parseInt($this.attr('width'), 10) : $this.width(), + aspectRatio = height / width; + if(!$this.attr('id')){ + var videoID = 'fitvid' + Math.floor(Math.random()*999999); + $this.attr('id', videoID); + } + $this.wrap('
').parent('.fluid-width-video-wrapper').css('padding-top', (aspectRatio * 100)+"%"); + $this.removeAttr('height').removeAttr('width'); + }); + }); + }; +})( jQuery ); diff --git a/widgets/less/functions.php b/widgets/less/functions.php new file mode 100644 index 000000000..73bd7bf59 --- /dev/null +++ b/widgets/less/functions.php @@ -0,0 +1,93 @@ +lum += $a2_value/100; + else $color->lum -= $a2_value/100; + + return array('raw_color', $color->hex); +} + +function origin_widgets_less_lumlighten($args){ + return origin_widgets_less_lum_change($args, 'lighten'); +} + +function origin_widgets_less_lumdarken($args){ + return origin_widgets_less_lum_change($args, 'darken'); +} + +/** + * Less handler function for texture function + * + * @param $texture + * @return string + */ +function origin_widgets_less_texture($texture){ + if($texture[0] != 'list') return ''; + + $return = ''; + foreach($texture[2] as $arg) { + if($arg[0] == 'keyword') { + $t = $arg[1]; + if($t == 'none') continue; + foreach(SiteOrigin_Panels_Widget::get_image_folders() as $folder => $folder_url) { + if(file_exists($folder.'/textures/'.$t.'.png')) { + $return .= 'url('.esc_url($folder_url.'/textures/'.$t.'.png').') repeat '; + break; + } + } + } + elseif($arg[0] == 'raw_color') { + $return .= $arg[1].' '; + } + } + return trim($return); +} + +/** + * Less handler function for widgetimage function + * + * @param $url + * @return string + */ +function origin_widgets_less_widgetimage($url){ + $the_url = ''; + foreach($url[2] as $p){ + if(is_string($p)){ + $the_url .= $p; + } + elseif(is_array($p)){ + $the_url .= $p[1]; + } + } + + // Search for the appropriate image + $return_url = ''; + foreach(SiteOrigin_Panels_Widget::get_image_folders() as $folder => $folder_url) { + if(file_exists($folder.'/'.$the_url)) { + $return_url = $folder_url.'/'.$the_url; + } + } + + if ( is_ssl() ) { + $return_url = str_replace('http://', 'https://', $return_url); + } + + return $return_url; +} \ No newline at end of file diff --git a/widgets/less/mixins.less b/widgets/less/mixins.less new file mode 100755 index 000000000..311b74a1b --- /dev/null +++ b/widgets/less/mixins.less @@ -0,0 +1,183 @@ +.gradient(@color: #F5F5F5, @start: #EEE, @stop: #FFF) { + background: @color; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, @start), color-stop(1, @stop)); + background: -ms-linear-gradient(bottom,@start,@stop); + background: -moz-linear-gradient(center bottom,@start 0%,@stop 100%); + background: -o-linear-gradient(@stop,@start); +} + +.bw-gradient(@color: #F5F5F5, @start: 0, @stop: 255) { + background: @color; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, rgb(@start,@start,@start)), color-stop(1, rgb(@stop,@stop,@stop))); + background: -ms-linear-gradient(bottom, rgb(@start,@start,@start) 0%, rgb(@stop,@stop,@stop) 100%); + background: -moz-linear-gradient(center bottom, rgb(@start,@start,@start) 0%, rgb(@stop,@stop,@stop) 100%); + background: -o-linear-gradient(rgb(@stop,@stop,@stop), rgb(@start,@start,@start)); + background: linear-gradient(rgb(@stop,@stop,@stop), rgb(@start,@start,@start)); +} + +.linear-gradient(@color, @gradient) { + background: @color; + background: -moz-linear-gradient(@gradient); + background: -webkit-linear-gradient(@gradient); + background: -o-linear-gradient(@gradient); + background: -ms-linear-gradient(@gradient); + background: linear-gradient(@gradient); +} + +.bordered(@top-color: #EEE, @right-color: #EEE, @bottom-color: #EEE, @left-color: #EEE) { + border-top: solid 1px @top-color; + border-left: solid 1px @left-color; + border-right: solid 1px @right-color; + border-bottom: solid 1px @bottom-color; +} + +.drop-shadow(@x-axis: 0, @y-axis: 1px, @blur: 2px, @alpha: 0.1) { + -webkit-box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha); + -moz-box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha); + box-shadow: @x-axis @y-axis @blur rgba(0, 0, 0, @alpha); +} + +.box-shadow(@shadow) { + -webkit-box-shadow: @shadow; + -moz-box-shadow: @shadow; + box-shadow: @shadow; +} + +.rounded(@radius: 2px) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; +} + +.border-radius(@topright: 0, @bottomright: 0, @bottomleft: 0, @topleft: 0) { + -webkit-border-top-right-radius: @topright; + -webkit-border-bottom-right-radius: @bottomright; + -webkit-border-bottom-left-radius: @bottomleft; + -webkit-border-top-left-radius: @topleft; + -moz-border-radius-topright: @topright; + -moz-border-radius-bottomright: @bottomright; + -moz-border-radius-bottomleft: @bottomleft; + -moz-border-radius-topleft: @topleft; + border-top-right-radius: @topright; + border-bottom-right-radius: @bottomright; + border-bottom-left-radius: @bottomleft; + border-top-left-radius: @topleft; + .background-clip(padding-box); +} + +.opacity(@opacity: 0.5) { + -moz-opacity: @opacity; + -khtml-opacity: @opacity; + -webkit-opacity: @opacity; + opacity: @opacity; + @opperc: @opacity * 100; + -ms-filter: ~"progid:DXImageTransform.Microsoft.Alpha(opacity=@{opperc})"; + filter: ~"alpha(opacity=@{opperc})"; +} + +.transition-duration(@duration: 0.2s) { + -moz-transition-duration: @duration; + -webkit-transition-duration: @duration; + -o-transition-duration: @duration; + transition-duration: @duration; +} + +.transform(...) { + -webkit-transform: @arguments; + -moz-transform: @arguments; + -o-transform: @arguments; + -ms-transform: @arguments; + transform: @arguments; +} + +.rotation(@deg:5deg) { + .transform(rotate(@deg)); +} + +.scale(@ratio:1.5) { + .transform(scale(@ratio)); +} + +.transition(@duration:0.2s, @on: all, @ease:ease) { + -webkit-transition: @on @duration @ease; + -moz-transition: @on @duration @ease; + -o-transition: @on @duration @ease; + transition: @on @duration @ease; +} + +.inner-shadow(@horizontal:0, @vertical:1px, @blur:2px, @alpha: 0.4) { + -webkit-box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha); + -moz-box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha); + box-shadow: inset @horizontal @vertical @blur rgba(0, 0, 0, @alpha); +} + +.box-sizing(@sizing: border-box) { + -ms-box-sizing: @sizing; + -moz-box-sizing: @sizing; + -webkit-box-sizing: @sizing; + box-sizing: @sizing; +} + +.user-select(@argument: none) { + -webkit-user-select: @argument; + -moz-user-select: @argument; + -ms-user-select: @argument; + user-select: @argument; +} + +.columns(@colwidth: 250px, @colcount: 0, @colgap: 50px, @columnRuleColor: #EEE, @columnRuleStyle: solid, @columnRuleWidth: 1px) { + -moz-column-width: @colwidth; + -moz-column-count: @colcount; + -moz-column-gap: @colgap; + -moz-column-rule-color: @columnRuleColor; + -moz-column-rule-style: @columnRuleStyle; + -moz-column-rule-width: @columnRuleWidth; + -webkit-column-width: @colwidth; + -webkit-column-count: @colcount; + -webkit-column-gap: @colgap; + -webkit-column-rule-color: @columnRuleColor; + -webkit-column-rule-style: @columnRuleStyle; + -webkit-column-rule-width: @columnRuleWidth; + column-width: @colwidth; + column-count: @colcount; + column-gap: @colgap; + column-rule-color: @columnRuleColor; + column-rule-style: @columnRuleStyle; + column-rule-width: @columnRuleWidth; +} + +.translate(@x:0, @y:0) { + .transform(translate(@x, @y)); +} + +.background-clip(@argument: padding-box) { + -moz-background-clip: @argument; + -webkit-background-clip: @argument; + background-clip: @argument; +} + +.clearfix() { + zoom: 1; + &:before { + content: ''; + display: block; + } + &:after { + content: ''; + display: table; + clear: both; + } +} + +.button-style(@base_color, @bg_var: 3.5, @border_darken: 18, @border_var: 4) { + @gradient_start: lumdarken(@base_color, @bg_var); + @gradient_end: lumlighten(@base_color, @bg_var); + + @border_color: lumdarken(@base_color, @border_darken); + + @border_top: lumlighten(@border_color, @border_var ); + @border_bottom: lumdarken(@border_color, @border_var ); + + .gradient(@base_color, @gradient_start, @gradient_end); + .bordered(@border_top, @border_color, @border_bottom, @border_color); +} \ No newline at end of file diff --git a/widgets/lib/color.php b/widgets/lib/color.php new file mode 100644 index 000000000..45c179b50 --- /dev/null +++ b/widgets/lib/color.php @@ -0,0 +1,474 @@ +> 0x10); + $rgb[1] = 0xFF & ($color_val >> 0x8); + $rgb[2] = 0xFF & $color_val; + } + elseif (strlen($hex) == 3) { //if shorthand notation, need some string manipulations + $rgb[0] = hexdec(str_repeat(substr($hex, 0, 1), 2)); + $rgb[1] = hexdec(str_repeat(substr($hex, 1, 1), 2)); + $rgb[2] = hexdec(str_repeat(substr($hex, 2, 1), 2)); + } + else { + throw new Exception('Invalid hex color'); + } + + foreach($rgb as $i => $p) $rgb[$i] = self::maxmin(round($p),0,255); + return $rgb; + } + + /** + * Convert RGB to HEX + */ + public static function rgb2hex($rgb){ + $hex = '#'; + foreach($rgb as $p){ + $p = base_convert($p,10,16); + $p = str_pad($p,2,'0',STR_PAD_LEFT); + $hex .= $p; + } + return strtoupper($hex); + } + + /** + * Convert a HSV color to an RGB color. + * + * @param array $hsv HSV array with values 0-1 + */ + public static function hsv2rgb ($hsv) + { + // The return RGB value + $rgb = array(); + + if($hsv[1] == 0){ + $rgb = array_fill(0,3,$hsv[2] * 255); + } + else{ + // Break hue into 6 possible segments + $hue = $hsv[0] * 6; + $hue_range = floor( $hue ); + + $v = array( + $hsv[2] * ( 1 - $hsv[1] ), + $hsv[2] * ( 1 - $hsv[1] * ( $hue - $hue_range ) ), + $hsv[2] * (1 - $hsv[1] * (1 - ($hue-$hue_range))) + ); + + switch($hue_range){ + case 0: + $rgb[0] = $hsv[2]; $rgb[1] = $v[2]; $rgb[2] = $v[0]; + break; + case 1: + $rgb[0] = $v[1]; $rgb[1] = $hsv[2]; $rgb[2] = $v[0]; + break; + case 2: + $rgb[0] = $v[0]; $rgb[1] = $hsv[2]; $rgb[2] = $v[2]; + break; + case 3: + $rgb[0] = $v[0]; $rgb[1] = $v[1]; $rgb[2] = $hsv[2]; + break; + case 4: + $rgb[0] = $v[2]; $rgb[1] = $v[0]; $rgb[2] = $hsv[2]; + break; + default : + $rgb[0] = $hsv[2]; $rgb[1] = $v[0]; $rgb[2] = $v[1]; + break; + } + + $rgb[0] = round($rgb[0] * 255); + $rgb[1] = round($rgb[1] * 255); + $rgb[2] = round($rgb[2] * 255); + } + + // Make sure the parts are in the proper range + foreach($rgb as $i => $p) $rgb[$i] = self::maxmin(round($p),0,255); + return $rgb; + } + + /** + * Converts an RGB color to an XYZ color. + * + * @param array $color The input color. Values from 0-255. + */ + public static function rgb2xyz(array $rgb) + { + foreach($rgb as $i => $c) $rgb[$i] /= 255; + + foreach($rgb as $i => $c){ + if ($c > 0.04045){ $rgb[$i] = pow(($c + 0.055) / 1.055, 2.4); } + else { $rgb[$i] = $c / 12.92; } + + $rgb[$i] = $rgb[$i] * 100; + } + + //Observer. = 2¡, Illuminant = D65 + $xyz = array(0,0,0); + $xyz[0] = $rgb[0] * 0.4124 + $rgb[1] * 0.3576 + $rgb[2] * 0.1805; + $xyz[1] = $rgb[0] * 0.2126 + $rgb[1] * 0.7152 + $rgb[2] * 0.0722; + $xyz[2] = $rgb[0] * 0.0193 + $rgb[1] * 0.1192 + $rgb[2] * 0.9505; + + return $xyz; + } + + /** + * Convert a RGB color to a HSV color + * + * @param array $rgb RGB array with values 0-255 + */ + public static function rgb2hsv ($rgb) + { + $rgb = self::rgb($rgb); + + $rgb[0] = ($rgb[0] / 255); + $rgb[1] = ($rgb[1] / 255); + $rgb[2] = ($rgb[2] / 255); + + $min = min($rgb[0], $rgb[1], $rgb[2]); + $max = max($rgb[0], $rgb[1], $rgb[2]); + $del_max = $max - $min; + + $hsv = array(0,0,$max); + + if ($del_max != 0){ + $hsv[1] = $del_max / $max; + + $del_r = ( ( ( $del_max - $rgb[0] ) / 6 ) + ( $del_max / 2 ) ) / $del_max; + $del_g = ( ( ( $del_max - $rgb[1] ) / 6 ) + ( $del_max / 2 ) ) / $del_max; + $del_b = ( ( ( $del_max - $rgb[2] ) / 6 ) + ( $del_max / 2 ) ) / $del_max; + + if ($rgb[0] == $max) $hsv[0] = $del_b - $del_g; + else if ($rgb[1] == $max) $hsv[0] = ( 1 / 3 ) + $del_r - $del_b; + else if ($rgb[2] == $max) $hsv[0] = ( 2 / 3 ) + $del_g - $del_r; + + if ($hsv[0] < 0) $hsv[0]++; + if ($hsv[0] > 1) $hsv[0]--; + } + + return $hsv; + } + + /** + * Converts a LAB color into RGB + */ + public static function lab2xyz(array $lab) + { + foreach($lab as $i => $c) $lab[$i] *= 100; + + // Observer= 2¡, Illuminant= D65 + $REF_X = 95.047; + $REF_Y = 100.000; + $REF_Z = 108.883; + + $xyz = array(); + + $xyz[1] = ($lab[0] + 16) / 116; + $xyz[0] = $lab[1] / 500 + $xyz[1]; + $xyz[2] = $xyz[1] - $lab[2] / 200; + + foreach($xyz as $i => $c){ + if ( pow( $c , 3 ) > 0.008856 ) { $xyz[$i] = pow( $c , 3 ); } + else { $xyz[$i] = ( $c - 16 / 116 ) / 7.787; } + } + + $xyz[0] *= $REF_X; + $xyz[1] *= $REF_Y; + $xyz[2] *= $REF_Z; + + return $xyz; + } + + + /** + * Convert XYZ color to a LAB color + */ + public static function xyz2lab(array $xyz) + { + // Observer= 2¡, Illuminant= D65 + $REF_X = 95.047; + $REF_Y = 100.000; + $REF_Z = 108.883; + + $xyz[0] = $xyz[0] / $REF_X; + $xyz[1] = $xyz[1] / $REF_Y; + $xyz[2] = $xyz[2] / $REF_Z; + + foreach($xyz as $i => $c){ + if ($c > 0.008856 ) { $xyz[$i] = pow( $c , 1/3 ); } + else { $xyz[$i] = ( 7.787 * $c ) + ( 16/116 ); } + } + + $lab = array(); + $lab[0] = ( 116 * $xyz[1] ) - 16; + $lab[1] = 500 * ( $xyz[0] - $xyz[1] ); + $lab[2] = 200 * ( $xyz[1] - $xyz[2] ); + + foreach($lab as $i => $c) $lab[$i] /= 100; + + return $lab; + } + + /** + * Convert an XYZ color to an RGB color + */ + public static function xyz2rgb($xyz) + { + // (Observer = 2¡, Illuminant = D65) + $xyz[0] /= 100; //X from 0 to 95.047 + $xyz[1] /= 100; //Y from 0 to 100.000 + $xyz[2] /= 100; //Z from 0 to 108.883 + + $rgb = array(); + + $rgb[0] = $xyz[0] * 3.2406 + $xyz[1] * -1.5372 + $xyz[2] * -0.4986; + $rgb[1] = $xyz[0] * -0.9689 + $xyz[1] * 1.8758 + $xyz[2] * 0.0415; + $rgb[2] = $xyz[0] * 0.0557 + $xyz[1] * -0.2040 + $xyz[2] * 1.0570; + + foreach($rgb as $i => $c){ + if ( $c > 0.0031308 ) { $rgb[$i] = 1.055 * pow( $c , ( 1 / 2.4 ) ) - 0.055; } + else { $rgb[$i] = 12.92 * $c; } + } + + $rgb[0] = round(min(max($rgb[0],0),1) * 255); + $rgb[1] = round(min(max($rgb[1],0),1) * 255); + $rgb[2] = round(min(max($rgb[2],0),1) * 255); + + return $rgb; + } + + // Combine the primary functions to create all 6 conversion functions + + /** + * Convert an RGB color to a LAB color. + */ + public static function rgb2lab($rgb) + { + $xyx = self::rgb2xyz(self::rgb($rgb)); + return self::xyz2lab($xyx); + } + + /** + * Convert a LAB color to a + */ + public static function lab2rgb($lab) + { + $xyx = self::lab2xyz($lab); + return self::xyz2rgb($xyx); + } + + /** + * Convert a LAB color to HSV + */ + public static function lab2hsv($lab) + { + $rgb = self::lab2rgb($lab); + return self::rgb2hsv($rgb); + } + + /** + * Convert an HSV color to LAB + */ + public static function hsv2lab($hsv) + { + $rgb = self::hsv2rgb($hsv); + return self::rgb2lab($rgb); + } + + /** + * Makes sure that the given value falls inside a range. + */ + public static function maxmin($i, $min, $max){ + return min(max($i,$min),$max); + } + + public static function float2hex($float){ + $hsv = array( + 0, + 0, + $float + ); + + return self::rgb2hex(self::hsv2rgb($hsv)); + } +} + +/** + * A color conversions class. Of course, you really spell it colour. Color conversion based on algorithms form EasyRGB . + * + * @author Greg Priday + * @copyright Copyright (c) 2011, Greg Priday + * @license GPL + */ +class SiteOrigin_Color_Object extends SiteOrigin_Color{ + private $changed; + + /** + * The hex value of this color before it was varied. + */ + private $color; + private $type; + + const COLOR_HSV = 'hsv'; + const COLOR_RGB = 'rgb'; + const COLOR_LAB = 'lab'; + + const COLOR_GREY = 'grey'; + const COLOR_HEX = 'hex'; + + const COLOR_RGB_R = 'red'; + const COLOR_RGB_G = 'green'; + const COLOR_RGB_B = 'blue'; + + const COLOR_LAB_L = 'lum'; + const COLOR_LAB_A = 'a'; + const COLOR_LAB_B = 'b'; + + const COLOR_HSV_H = 'hue'; + const COLOR_HSV_S = 'sat'; + const COLOR_HSV_V = 'val'; + + function __construct($color, $type = self::COLOR_HEX){ + if($type == self::COLOR_HEX){ + $this->type = self::COLOR_RGB; + $this->color = self::hex2rgb($color); + } + elseif(is_numeric($color) && $type == self::COLOR_GREY){ + // We're going to assume this is a greyscale color + $this->type = self::COLOR_HSV; + $this->color = array(1,0,min(max($color,0),1)); + } + elseif($type == self::COLOR_GREY){ + if(!is_int($color)) throw Exception('Invalid color'); + $this->type = self::COLOR_RGB; + $this->color = array($color,$color,$color); + } + else{ + $this->color = $color; + $this->type = $type; + } + + $this->changed = array(); + } + + /** + * Get a color or color part + */ + public function __get($name) + { + $colors = array( + self::COLOR_HSV => array(self::COLOR_HSV_H, self::COLOR_HSV_S, self::COLOR_HSV_V), + self::COLOR_RGB => array(self::COLOR_RGB_R, self::COLOR_RGB_G, self::COLOR_RGB_B), + self::COLOR_LAB => array(self::COLOR_LAB_L, self::COLOR_LAB_A, self::COLOR_LAB_B) + ); + + if($name == 'hex') { + return self::rgb2hex($this->rgb); + } + elseif(in_array($name, array_keys($colors))){ + // We need a color array + if($name == $this->type) return $this->color; + else{ + $func = $this->type.'2'.$name; + return call_user_func(array($this,$func), $this->color); + } + } + else{ + // We need an individual color element + foreach($colors as $type => $parts){ + if(in_array($name, $parts)){ + $color = $this->{$type}; + $i = array_search($name, $parts); + return $color[$i]; + } + } + } + + } + + /** + * Set a color or color part. + */ + public function __set($name, $value) + { + $this->changed[] = $name; + + $colors = array( + self::COLOR_HSV => array(self::COLOR_HSV_H, self::COLOR_HSV_S, self::COLOR_HSV_V), + self::COLOR_RGB => array(self::COLOR_RGB_R, self::COLOR_RGB_G, self::COLOR_RGB_B), + self::COLOR_LAB => array(self::COLOR_LAB_L, self::COLOR_LAB_A, self::COLOR_LAB_B) + ); + + if($name == 'hex'){ + $this->type = 'rgb'; + $this->color = self::hex2rgb($value); + } + elseif(in_array($name, array_keys($colors))){ + $this->type = $name; + $this->color = $value; + } + else{ + foreach($colors as $type => $parts){ + if(in_array($name, $parts)){ + $color = $this->{$type}; + $i = array_search($name, $parts); + $color[$i] = $value; + + $this->type = $type; + $this->color = $color; + } + } + } + } + + /** + * @return array + */ + public function get_changed(){ + return $this->changed; + } + + public function __toString() { + return $this->hex; + } + + /** + * Calculates the percieved difference between 2 colors. + */ + public static function distance(SiteOrigin_Color_Object $c1, SiteOrigin_Color_Object $c2){ + return sqrt( + pow($c1->lab[0]-$c2->lab[0],2) + + pow($c1->lab[1]-$c2->lab[1],2) + + pow($c1->lab[2]-$c2->lab[2],2) + ); + } +} \ No newline at end of file diff --git a/widgets/lib/lessc.inc.php b/widgets/lib/lessc.inc.php new file mode 100644 index 000000000..411ca2fab --- /dev/null +++ b/widgets/lib/lessc.inc.php @@ -0,0 +1,3473 @@ + + * Licensed under MIT or GPLv3, see LICENSE + */ + + +/** + * The less compiler and parser. + * + * Converting LESS to CSS is a three stage process. The incoming file is parsed + * by `lessc_parser` into a syntax tree, then it is compiled into another tree + * representing the CSS structure by `lessc`. The CSS tree is fed into a + * formatter, like `lessc_formatter` which then outputs CSS as a string. + * + * During the first compile, all values are *reduced*, which means that their + * types are brought to the lowest form before being dump as strings. This + * handles math equations, variable dereferences, and the like. + * + * The `parse` function of `lessc` is the entry point. + * + * In summary: + * + * The `lessc` class creates an intstance of the parser, feeds it LESS code, + * then transforms the resulting tree to a CSS tree. This class also holds the + * evaluation context, such as all available mixins and variables at any given + * time. + * + * The `lessc_parser` class is only concerned with parsing its input. + * + * The `lessc_formatter` takes a CSS tree, and dumps it to a formatted string, + * handling things like indentation. + */ +class lessc { + static public $VERSION = "v0.3.9"; + static protected $TRUE = array("keyword", "true"); + static protected $FALSE = array("keyword", "false"); + + protected $libFunctions = array(); + protected $registeredVars = array(); + protected $preserveComments = false; + + public $vPrefix = '@'; // prefix of abstract properties + public $mPrefix = '$'; // prefix of abstract blocks + public $parentSelector = '&'; + + public $importDisabled = false; + public $importDir = ''; + + protected $numberPrecision = null; + + // set to the parser that generated the current line when compiling + // so we know how to create error messages + protected $sourceParser = null; + protected $sourceLoc = null; + + static public $defaultValue = array("keyword", ""); + + static protected $nextImportId = 0; // uniquely identify imports + + // attempts to find the path of an import url, returns null for css files + protected function findImport($url) { + foreach ((array)$this->importDir as $dir) { + $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url; + if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) { + return $file; + } + } + + return null; + } + + protected function fileExists($name) { + return is_file($name); + } + + static public function compressList($items, $delim) { + if (!isset($items[1]) && isset($items[0])) return $items[0]; + else return array('list', $delim, $items); + } + + static public function preg_quote($what) { + return preg_quote($what, '/'); + } + + protected function tryImport($importPath, $parentBlock, $out) { + if ($importPath[0] == "function" && $importPath[1] == "url") { + $importPath = $this->flattenList($importPath[2]); + } + + $str = $this->coerceString($importPath); + if ($str === null) return false; + + $url = $this->compileValue($this->lib_e($str)); + + // don't import if it ends in css + if (substr_compare($url, '.css', -4, 4) === 0) return false; + + $realPath = $this->findImport($url); + if ($realPath === null) return false; + + if ($this->importDisabled) { + return array(false, "/* import disabled */"); + } + + $this->addParsedFile($realPath); + $parser = $this->makeParser($realPath); + $root = $parser->parse(file_get_contents($realPath)); + + // set the parents of all the block props + foreach ($root->props as $prop) { + if ($prop[0] == "block") { + $prop[1]->parent = $parentBlock; + } + } + + // copy mixins into scope, set their parents + // bring blocks from import into current block + // TODO: need to mark the source parser these came from this file + foreach ($root->children as $childName => $child) { + if (isset($parentBlock->children[$childName])) { + $parentBlock->children[$childName] = array_merge( + $parentBlock->children[$childName], + $child); + } else { + $parentBlock->children[$childName] = $child; + } + } + + $pi = pathinfo($realPath); + $dir = $pi["dirname"]; + + list($top, $bottom) = $this->sortProps($root->props, true); + $this->compileImportedProps($top, $parentBlock, $out, $parser, $dir); + + return array(true, $bottom, $parser, $dir); + } + + protected function compileImportedProps($props, $block, $out, $sourceParser, $importDir) { + $oldSourceParser = $this->sourceParser; + + $oldImport = $this->importDir; + + // TODO: this is because the importDir api is stupid + $this->importDir = (array)$this->importDir; + array_unshift($this->importDir, $importDir); + + foreach ($props as $prop) { + $this->compileProp($prop, $block, $out); + } + + $this->importDir = $oldImport; + $this->sourceParser = $oldSourceParser; + } + + /** + * Recursively compiles a block. + * + * A block is analogous to a CSS block in most cases. A single LESS document + * is encapsulated in a block when parsed, but it does not have parent tags + * so all of it's children appear on the root level when compiled. + * + * Blocks are made up of props and children. + * + * Props are property instructions, array tuples which describe an action + * to be taken, eg. write a property, set a variable, mixin a block. + * + * The children of a block are just all the blocks that are defined within. + * This is used to look up mixins when performing a mixin. + * + * Compiling the block involves pushing a fresh environment on the stack, + * and iterating through the props, compiling each one. + * + * See lessc::compileProp() + * + */ + protected function compileBlock($block) { + switch ($block->type) { + case "root": + $this->compileRoot($block); + break; + case null: + $this->compileCSSBlock($block); + break; + case "media": + $this->compileMedia($block); + break; + case "directive": + $name = "@" . $block->name; + if (!empty($block->value)) { + $name .= " " . $this->compileValue($this->reduce($block->value)); + } + + $this->compileNestedBlock($block, array($name)); + break; + default: + $this->throwError("unknown block type: $block->type\n"); + } + } + + protected function compileCSSBlock($block) { + $env = $this->pushEnv(); + + $selectors = $this->compileSelectors($block->tags); + $env->selectors = $this->multiplySelectors($selectors); + $out = $this->makeOutputBlock(null, $env->selectors); + + $this->scope->children[] = $out; + $this->compileProps($block, $out); + + $block->scope = $env; // mixins carry scope with them! + $this->popEnv(); + } + + protected function compileMedia($media) { + $env = $this->pushEnv($media); + $parentScope = $this->mediaParent($this->scope); + + $query = $this->compileMediaQuery($this->multiplyMedia($env)); + + $this->scope = $this->makeOutputBlock($media->type, array($query)); + $parentScope->children[] = $this->scope; + + $this->compileProps($media, $this->scope); + + if (count($this->scope->lines) > 0) { + $orphanSelelectors = $this->findClosestSelectors(); + if (!is_null($orphanSelelectors)) { + $orphan = $this->makeOutputBlock(null, $orphanSelelectors); + $orphan->lines = $this->scope->lines; + array_unshift($this->scope->children, $orphan); + $this->scope->lines = array(); + } + } + + $this->scope = $this->scope->parent; + $this->popEnv(); + } + + protected function mediaParent($scope) { + while (!empty($scope->parent)) { + if (!empty($scope->type) && $scope->type != "media") { + break; + } + $scope = $scope->parent; + } + + return $scope; + } + + protected function compileNestedBlock($block, $selectors) { + $this->pushEnv($block); + $this->scope = $this->makeOutputBlock($block->type, $selectors); + $this->scope->parent->children[] = $this->scope; + + $this->compileProps($block, $this->scope); + + $this->scope = $this->scope->parent; + $this->popEnv(); + } + + protected function compileRoot($root) { + $this->pushEnv(); + $this->scope = $this->makeOutputBlock($root->type); + $this->compileProps($root, $this->scope); + $this->popEnv(); + } + + protected function compileProps($block, $out) { + foreach ($this->sortProps($block->props) as $prop) { + $this->compileProp($prop, $block, $out); + } + } + + protected function sortProps($props, $split = false) { + $vars = array(); + $imports = array(); + $other = array(); + + foreach ($props as $prop) { + switch ($prop[0]) { + case "assign": + if (isset($prop[1][0]) && $prop[1][0] == $this->vPrefix) { + $vars[] = $prop; + } else { + $other[] = $prop; + } + break; + case "import": + $id = self::$nextImportId++; + $prop[] = $id; + $imports[] = $prop; + $other[] = array("import_mixin", $id); + break; + default: + $other[] = $prop; + } + } + + if ($split) { + return array(array_merge($vars, $imports), $other); + } else { + return array_merge($vars, $imports, $other); + } + } + + protected function compileMediaQuery($queries) { + $compiledQueries = array(); + foreach ($queries as $query) { + $parts = array(); + foreach ($query as $q) { + switch ($q[0]) { + case "mediaType": + $parts[] = implode(" ", array_slice($q, 1)); + break; + case "mediaExp": + if (isset($q[2])) { + $parts[] = "($q[1]: " . + $this->compileValue($this->reduce($q[2])) . ")"; + } else { + $parts[] = "($q[1])"; + } + break; + case "variable": + $parts[] = $this->compileValue($this->reduce($q)); + break; + } + } + + if (count($parts) > 0) { + $compiledQueries[] = implode(" and ", $parts); + } + } + + $out = "@media"; + if (!empty($parts)) { + $out .= " " . + implode($this->formatter->selectorSeparator, $compiledQueries); + } + return $out; + } + + protected function multiplyMedia($env, $childQueries = null) { + if (is_null($env) || + !empty($env->block->type) && $env->block->type != "media") + { + return $childQueries; + } + + // plain old block, skip + if (empty($env->block->type)) { + return $this->multiplyMedia($env->parent, $childQueries); + } + + $out = array(); + $queries = $env->block->queries; + if (is_null($childQueries)) { + $out = $queries; + } else { + foreach ($queries as $parent) { + foreach ($childQueries as $child) { + $out[] = array_merge($parent, $child); + } + } + } + + return $this->multiplyMedia($env->parent, $out); + } + + protected function expandParentSelectors(&$tag, $replace) { + $parts = explode("$&$", $tag); + $count = 0; + foreach ($parts as &$part) { + $part = str_replace($this->parentSelector, $replace, $part, $c); + $count += $c; + } + $tag = implode($this->parentSelector, $parts); + return $count; + } + + protected function findClosestSelectors() { + $env = $this->env; + $selectors = null; + while ($env !== null) { + if (isset($env->selectors)) { + $selectors = $env->selectors; + break; + } + $env = $env->parent; + } + + return $selectors; + } + + + // multiply $selectors against the nearest selectors in env + protected function multiplySelectors($selectors) { + // find parent selectors + + $parentSelectors = $this->findClosestSelectors(); + if (is_null($parentSelectors)) { + // kill parent reference in top level selector + foreach ($selectors as &$s) { + $this->expandParentSelectors($s, ""); + } + + return $selectors; + } + + $out = array(); + foreach ($parentSelectors as $parent) { + foreach ($selectors as $child) { + $count = $this->expandParentSelectors($child, $parent); + + // don't prepend the parent tag if & was used + if ($count > 0) { + $out[] = trim($child); + } else { + $out[] = trim($parent . ' ' . $child); + } + } + } + + return $out; + } + + // reduces selector expressions + protected function compileSelectors($selectors) { + $out = array(); + + foreach ($selectors as $s) { + if (is_array($s)) { + list(, $value) = $s; + $out[] = trim($this->compileValue($this->reduce($value))); + } else { + $out[] = $s; + } + } + + return $out; + } + + protected function eq($left, $right) { + return $left == $right; + } + + protected function patternMatch($block, $callingArgs) { + // match the guards if it has them + // any one of the groups must have all its guards pass for a match + if (!empty($block->guards)) { + $groupPassed = false; + foreach ($block->guards as $guardGroup) { + foreach ($guardGroup as $guard) { + $this->pushEnv(); + $this->zipSetArgs($block->args, $callingArgs); + + $negate = false; + if ($guard[0] == "negate") { + $guard = $guard[1]; + $negate = true; + } + + $passed = $this->reduce($guard) == self::$TRUE; + if ($negate) $passed = !$passed; + + $this->popEnv(); + + if ($passed) { + $groupPassed = true; + } else { + $groupPassed = false; + break; + } + } + + if ($groupPassed) break; + } + + if (!$groupPassed) { + return false; + } + } + + $numCalling = count($callingArgs); + + if (empty($block->args)) { + return $block->isVararg || $numCalling == 0; + } + + $i = -1; // no args + // try to match by arity or by argument literal + foreach ($block->args as $i => $arg) { + switch ($arg[0]) { + case "lit": + if (empty($callingArgs[$i]) || !$this->eq($arg[1], $callingArgs[$i])) { + return false; + } + break; + case "arg": + // no arg and no default value + if (!isset($callingArgs[$i]) && !isset($arg[2])) { + return false; + } + break; + case "rest": + $i--; // rest can be empty + break 2; + } + } + + if ($block->isVararg) { + return true; // not having enough is handled above + } else { + $numMatched = $i + 1; + // greater than becuase default values always match + return $numMatched >= $numCalling; + } + } + + protected function patternMatchAll($blocks, $callingArgs) { + $matches = null; + foreach ($blocks as $block) { + if ($this->patternMatch($block, $callingArgs)) { + $matches[] = $block; + } + } + + return $matches; + } + + // attempt to find blocks matched by path and args + protected function findBlocks($searchIn, $path, $args, $seen=array()) { + if ($searchIn == null) return null; + if (isset($seen[$searchIn->id])) return null; + $seen[$searchIn->id] = true; + + $name = $path[0]; + + if (isset($searchIn->children[$name])) { + $blocks = $searchIn->children[$name]; + if (count($path) == 1) { + $matches = $this->patternMatchAll($blocks, $args); + if (!empty($matches)) { + // This will return all blocks that match in the closest + // scope that has any matching block, like lessjs + return $matches; + } + } else { + $matches = array(); + foreach ($blocks as $subBlock) { + $subMatches = $this->findBlocks($subBlock, + array_slice($path, 1), $args, $seen); + + if (!is_null($subMatches)) { + foreach ($subMatches as $sm) { + $matches[] = $sm; + } + } + } + + return count($matches) > 0 ? $matches : null; + } + } + + if ($searchIn->parent === $searchIn) return null; + return $this->findBlocks($searchIn->parent, $path, $args, $seen); + } + + // sets all argument names in $args to either the default value + // or the one passed in through $values + protected function zipSetArgs($args, $values) { + $i = 0; + $assignedValues = array(); + foreach ($args as $a) { + if ($a[0] == "arg") { + if ($i < count($values) && !is_null($values[$i])) { + $value = $values[$i]; + } elseif (isset($a[2])) { + $value = $a[2]; + } else $value = null; + + $value = $this->reduce($value); + $this->set($a[1], $value); + $assignedValues[] = $value; + } + $i++; + } + + // check for a rest + $last = end($args); + if ($last[0] == "rest") { + $rest = array_slice($values, count($args) - 1); + $this->set($last[1], $this->reduce(array("list", " ", $rest))); + } + + $this->env->arguments = $assignedValues; + } + + // compile a prop and update $lines or $blocks appropriately + protected function compileProp($prop, $block, $out) { + // set error position context + $this->sourceLoc = isset($prop[-1]) ? $prop[-1] : -1; + + switch ($prop[0]) { + case 'assign': + list(, $name, $value) = $prop; + if ($name[0] == $this->vPrefix) { + $this->set($name, $value); + } else { + $out->lines[] = $this->formatter->property($name, + $this->compileValue($this->reduce($value))); + } + break; + case 'block': + list(, $child) = $prop; + $this->compileBlock($child); + break; + case 'mixin': + list(, $path, $args, $suffix) = $prop; + + $args = array_map(array($this, "reduce"), (array)$args); + $mixins = $this->findBlocks($block, $path, $args); + + if ($mixins === null) { + // fwrite(STDERR,"failed to find block: ".implode(" > ", $path)."\n"); + break; // throw error here?? + } + + foreach ($mixins as $mixin) { + $haveScope = false; + if (isset($mixin->parent->scope)) { + $haveScope = true; + $mixinParentEnv = $this->pushEnv(); + $mixinParentEnv->storeParent = $mixin->parent->scope; + } + + $haveArgs = false; + if (isset($mixin->args)) { + $haveArgs = true; + $this->pushEnv(); + $this->zipSetArgs($mixin->args, $args); + } + + $oldParent = $mixin->parent; + if ($mixin != $block) $mixin->parent = $block; + + foreach ($this->sortProps($mixin->props) as $subProp) { + if ($suffix !== null && + $subProp[0] == "assign" && + is_string($subProp[1]) && + $subProp[1]{0} != $this->vPrefix) + { + $subProp[2] = array( + 'list', ' ', + array($subProp[2], array('keyword', $suffix)) + ); + } + + $this->compileProp($subProp, $mixin, $out); + } + + $mixin->parent = $oldParent; + + if ($haveArgs) $this->popEnv(); + if ($haveScope) $this->popEnv(); + } + + break; + case 'raw': + $out->lines[] = $prop[1]; + break; + case "directive": + list(, $name, $value) = $prop; + $out->lines[] = "@$name " . $this->compileValue($this->reduce($value)).';'; + break; + case "comment": + $out->lines[] = $prop[1]; + break; + case "import"; + list(, $importPath, $importId) = $prop; + $importPath = $this->reduce($importPath); + + if (!isset($this->env->imports)) { + $this->env->imports = array(); + } + + $result = $this->tryImport($importPath, $block, $out); + + $this->env->imports[$importId] = $result === false ? + array(false, "@import " . $this->compileValue($importPath).";") : + $result; + + break; + case "import_mixin": + list(,$importId) = $prop; + $import = $this->env->imports[$importId]; + if ($import[0] === false) { + $out->lines[] = $import[1]; + } else { + list(, $bottom, $parser, $importDir) = $import; + $this->compileImportedProps($bottom, $block, $out, $parser, $importDir); + } + + break; + default: + $this->throwError("unknown op: {$prop[0]}\n"); + } + } + + + /** + * Compiles a primitive value into a CSS property value. + * + * Values in lessphp are typed by being wrapped in arrays, their format is + * typically: + * + * array(type, contents [, additional_contents]*) + * + * The input is expected to be reduced. This function will not work on + * things like expressions and variables. + */ + protected function compileValue($value) { + switch ($value[0]) { + case 'list': + // [1] - delimiter + // [2] - array of values + return implode($value[1], array_map(array($this, 'compileValue'), $value[2])); + case 'raw_color': + if (!empty($this->formatter->compressColors)) { + return $this->compileValue($this->coerceColor($value)); + } + return $value[1]; + case 'keyword': + // [1] - the keyword + return $value[1]; + case 'number': + list(, $num, $unit) = $value; + // [1] - the number + // [2] - the unit + if ($this->numberPrecision !== null) { + $num = round($num, $this->numberPrecision); + } + return $num . $unit; + case 'string': + // [1] - contents of string (includes quotes) + list(, $delim, $content) = $value; + foreach ($content as &$part) { + if (is_array($part)) { + $part = $this->compileValue($part); + } + } + return $delim . implode($content) . $delim; + case 'color': + // [1] - red component (either number or a %) + // [2] - green component + // [3] - blue component + // [4] - optional alpha component + list(, $r, $g, $b) = $value; + $r = round($r); + $g = round($g); + $b = round($b); + + if (count($value) == 5 && $value[4] != 1) { // rgba + return 'rgba('.$r.','.$g.','.$b.','.$value[4].')'; + } + + $h = sprintf("#%02x%02x%02x", $r, $g, $b); + + if (!empty($this->formatter->compressColors)) { + // Converting hex color to short notation (e.g. #003399 to #039) + if ($h[1] === $h[2] && $h[3] === $h[4] && $h[5] === $h[6]) { + $h = '#' . $h[1] . $h[3] . $h[5]; + } + } + + return $h; + + case 'function': + list(, $name, $args) = $value; + return $name.'('.$this->compileValue($args).')'; + default: // assumed to be unit + $this->throwError("unknown value type: $value[0]"); + } + } + + protected function lib_isnumber($value) { + return $this->toBool($value[0] == "number"); + } + + protected function lib_isstring($value) { + return $this->toBool($value[0] == "string"); + } + + protected function lib_iscolor($value) { + return $this->toBool($this->coerceColor($value)); + } + + protected function lib_iskeyword($value) { + return $this->toBool($value[0] == "keyword"); + } + + protected function lib_ispixel($value) { + return $this->toBool($value[0] == "number" && $value[2] == "px"); + } + + protected function lib_ispercentage($value) { + return $this->toBool($value[0] == "number" && $value[2] == "%"); + } + + protected function lib_isem($value) { + return $this->toBool($value[0] == "number" && $value[2] == "em"); + } + + protected function lib_isrem($value) { + return $this->toBool($value[0] == "number" && $value[2] == "rem"); + } + + protected function lib_rgbahex($color) { + $color = $this->coerceColor($color); + if (is_null($color)) + $this->throwError("color expected for rgbahex"); + + return sprintf("#%02x%02x%02x%02x", + isset($color[4]) ? $color[4]*255 : 255, + $color[1],$color[2], $color[3]); + } + + protected function lib_argb($color){ + return $this->lib_rgbahex($color); + } + + // utility func to unquote a string + protected function lib_e($arg) { + switch ($arg[0]) { + case "list": + $items = $arg[2]; + if (isset($items[0])) { + return $this->lib_e($items[0]); + } + return self::$defaultValue; + case "string": + $arg[1] = ""; + return $arg; + case "keyword": + return $arg; + default: + return array("keyword", $this->compileValue($arg)); + } + } + + protected function lib__sprintf($args) { + if ($args[0] != "list") return $args; + $values = $args[2]; + $string = array_shift($values); + $template = $this->compileValue($this->lib_e($string)); + + $i = 0; + if (preg_match_all('/%[dsa]/', $template, $m)) { + foreach ($m[0] as $match) { + $val = isset($values[$i]) ? + $this->reduce($values[$i]) : array('keyword', ''); + + // lessjs compat, renders fully expanded color, not raw color + if ($color = $this->coerceColor($val)) { + $val = $color; + } + + $i++; + $rep = $this->compileValue($this->lib_e($val)); + $template = preg_replace('/'.self::preg_quote($match).'/', + $rep, $template, 1); + } + } + + $d = $string[0] == "string" ? $string[1] : '"'; + return array("string", $d, array($template)); + } + + protected function lib_floor($arg) { + $value = $this->assertNumber($arg); + return array("number", floor($value), $arg[2]); + } + + protected function lib_ceil($arg) { + $value = $this->assertNumber($arg); + return array("number", ceil($value), $arg[2]); + } + + protected function lib_round($arg) { + $value = $this->assertNumber($arg); + return array("number", round($value), $arg[2]); + } + + protected function lib_unit($arg) { + if ($arg[0] == "list") { + list($number, $newUnit) = $arg[2]; + return array("number", $this->assertNumber($number), + $this->compileValue($this->lib_e($newUnit))); + } else { + return array("number", $this->assertNumber($arg), ""); + } + } + + /** + * Helper function to get arguments for color manipulation functions. + * takes a list that contains a color like thing and a percentage + */ + protected function colorArgs($args) { + if ($args[0] != 'list' || count($args[2]) < 2) { + return array(array('color', 0, 0, 0), 0); + } + list($color, $delta) = $args[2]; + $color = $this->assertColor($color); + $delta = floatval($delta[1]); + + return array($color, $delta); + } + + protected function lib_darken($args) { + list($color, $delta) = $this->colorArgs($args); + + $hsl = $this->toHSL($color); + $hsl[3] = $this->clamp($hsl[3] - $delta, 100); + return $this->toRGB($hsl); + } + + protected function lib_lighten($args) { + list($color, $delta) = $this->colorArgs($args); + + $hsl = $this->toHSL($color); + $hsl[3] = $this->clamp($hsl[3] + $delta, 100); + return $this->toRGB($hsl); + } + + protected function lib_saturate($args) { + list($color, $delta) = $this->colorArgs($args); + + $hsl = $this->toHSL($color); + $hsl[2] = $this->clamp($hsl[2] + $delta, 100); + return $this->toRGB($hsl); + } + + protected function lib_desaturate($args) { + list($color, $delta) = $this->colorArgs($args); + + $hsl = $this->toHSL($color); + $hsl[2] = $this->clamp($hsl[2] - $delta, 100); + return $this->toRGB($hsl); + } + + protected function lib_spin($args) { + list($color, $delta) = $this->colorArgs($args); + + $hsl = $this->toHSL($color); + + $hsl[1] = $hsl[1] + $delta % 360; + if ($hsl[1] < 0) $hsl[1] += 360; + + return $this->toRGB($hsl); + } + + protected function lib_fadeout($args) { + list($color, $delta) = $this->colorArgs($args); + $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta/100); + return $color; + } + + protected function lib_fadein($args) { + list($color, $delta) = $this->colorArgs($args); + $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta/100); + return $color; + } + + protected function lib_hue($color) { + $hsl = $this->toHSL($this->assertColor($color)); + return round($hsl[1]); + } + + protected function lib_saturation($color) { + $hsl = $this->toHSL($this->assertColor($color)); + return round($hsl[2]); + } + + protected function lib_lightness($color) { + $hsl = $this->toHSL($this->assertColor($color)); + return round($hsl[3]); + } + + // get the alpha of a color + // defaults to 1 for non-colors or colors without an alpha + protected function lib_alpha($value) { + if (!is_null($color = $this->coerceColor($value))) { + return isset($color[4]) ? $color[4] : 1; + } + } + + // set the alpha of the color + protected function lib_fade($args) { + list($color, $alpha) = $this->colorArgs($args); + $color[4] = $this->clamp($alpha / 100.0); + return $color; + } + + protected function lib_percentage($arg) { + $num = $this->assertNumber($arg); + return array("number", $num*100, "%"); + } + + // mixes two colors by weight + // mix(@color1, @color2, @weight); + // http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method + protected function lib_mix($args) { + if ($args[0] != "list" || count($args[2]) < 3) + $this->throwError("mix expects (color1, color2, weight)"); + + list($first, $second, $weight) = $args[2]; + $first = $this->assertColor($first); + $second = $this->assertColor($second); + + $first_a = $this->lib_alpha($first); + $second_a = $this->lib_alpha($second); + $weight = $weight[1] / 100.0; + + $w = $weight * 2 - 1; + $a = $first_a - $second_a; + + $w1 = (($w * $a == -1 ? $w : ($w + $a)/(1 + $w * $a)) + 1) / 2.0; + $w2 = 1.0 - $w1; + + $new = array('color', + $w1 * $first[1] + $w2 * $second[1], + $w1 * $first[2] + $w2 * $second[2], + $w1 * $first[3] + $w2 * $second[3], + ); + + if ($first_a != 1.0 || $second_a != 1.0) { + $new[] = $first_a * $weight + $second_a * ($weight - 1); + } + + return $this->fixColor($new); + } + + protected function lib_contrast($args) { + if ($args[0] != 'list' || count($args[2]) < 3) { + return array(array('color', 0, 0, 0), 0); + } + + list($inputColor, $darkColor, $lightColor) = $args[2]; + + $inputColor = $this->assertColor($inputColor); + $darkColor = $this->assertColor($darkColor); + $lightColor = $this->assertColor($lightColor); + $hsl = $this->toHSL($inputColor); + + if ($hsl[3] > 50) { + return $darkColor; + } + + return $lightColor; + } + + protected function assertColor($value, $error = "expected color value") { + $color = $this->coerceColor($value); + if (is_null($color)) $this->throwError($error); + return $color; + } + + protected function assertNumber($value, $error = "expecting number") { + if ($value[0] == "number") return $value[1]; + $this->throwError($error); + } + + protected function toHSL($color) { + if ($color[0] == 'hsl') return $color; + + $r = $color[1] / 255; + $g = $color[2] / 255; + $b = $color[3] / 255; + + $min = min($r, $g, $b); + $max = max($r, $g, $b); + + $L = ($min + $max) / 2; + if ($min == $max) { + $S = $H = 0; + } else { + if ($L < 0.5) + $S = ($max - $min)/($max + $min); + else + $S = ($max - $min)/(2.0 - $max - $min); + + if ($r == $max) $H = ($g - $b)/($max - $min); + elseif ($g == $max) $H = 2.0 + ($b - $r)/($max - $min); + elseif ($b == $max) $H = 4.0 + ($r - $g)/($max - $min); + + } + + $out = array('hsl', + ($H < 0 ? $H + 6 : $H)*60, + $S*100, + $L*100, + ); + + if (count($color) > 4) $out[] = $color[4]; // copy alpha + return $out; + } + + protected function toRGB_helper($comp, $temp1, $temp2) { + if ($comp < 0) $comp += 1.0; + elseif ($comp > 1) $comp -= 1.0; + + if (6 * $comp < 1) return $temp1 + ($temp2 - $temp1) * 6 * $comp; + if (2 * $comp < 1) return $temp2; + if (3 * $comp < 2) return $temp1 + ($temp2 - $temp1)*((2/3) - $comp) * 6; + + return $temp1; + } + + /** + * Converts a hsl array into a color value in rgb. + * Expects H to be in range of 0 to 360, S and L in 0 to 100 + */ + protected function toRGB($color) { + if ($color[0] == 'color') return $color; + + $H = $color[1] / 360; + $S = $color[2] / 100; + $L = $color[3] / 100; + + if ($S == 0) { + $r = $g = $b = $L; + } else { + $temp2 = $L < 0.5 ? + $L*(1.0 + $S) : + $L + $S - $L * $S; + + $temp1 = 2.0 * $L - $temp2; + + $r = $this->toRGB_helper($H + 1/3, $temp1, $temp2); + $g = $this->toRGB_helper($H, $temp1, $temp2); + $b = $this->toRGB_helper($H - 1/3, $temp1, $temp2); + } + + // $out = array('color', round($r*255), round($g*255), round($b*255)); + $out = array('color', $r*255, $g*255, $b*255); + if (count($color) > 4) $out[] = $color[4]; // copy alpha + return $out; + } + + protected function clamp($v, $max = 1, $min = 0) { + return min($max, max($min, $v)); + } + + /** + * Convert the rgb, rgba, hsl color literals of function type + * as returned by the parser into values of color type. + */ + protected function funcToColor($func) { + $fname = $func[1]; + if ($func[2][0] != 'list') return false; // need a list of arguments + $rawComponents = $func[2][2]; + + if ($fname == 'hsl' || $fname == 'hsla') { + $hsl = array('hsl'); + $i = 0; + foreach ($rawComponents as $c) { + $val = $this->reduce($c); + $val = isset($val[1]) ? floatval($val[1]) : 0; + + if ($i == 0) $clamp = 360; + elseif ($i < 3) $clamp = 100; + else $clamp = 1; + + $hsl[] = $this->clamp($val, $clamp); + $i++; + } + + while (count($hsl) < 4) $hsl[] = 0; + return $this->toRGB($hsl); + + } elseif ($fname == 'rgb' || $fname == 'rgba') { + $components = array(); + $i = 1; + foreach ($rawComponents as $c) { + $c = $this->reduce($c); + if ($i < 4) { + if ($c[0] == "number" && $c[2] == "%") { + $components[] = 255 * ($c[1] / 100); + } else { + $components[] = floatval($c[1]); + } + } elseif ($i == 4) { + if ($c[0] == "number" && $c[2] == "%") { + $components[] = 1.0 * ($c[1] / 100); + } else { + $components[] = floatval($c[1]); + } + } else break; + + $i++; + } + while (count($components) < 3) $components[] = 0; + array_unshift($components, 'color'); + return $this->fixColor($components); + } + + return false; + } + + protected function reduce($value, $forExpression = false) { + switch ($value[0]) { + case "interpolate": + $reduced = $this->reduce($value[1]); + $var = $this->compileValue($reduced); + $res = $this->reduce(array("variable", $this->vPrefix . $var)); + + if (empty($value[2])) $res = $this->lib_e($res); + + return $res; + case "variable": + $key = $value[1]; + if (is_array($key)) { + $key = $this->reduce($key); + $key = $this->vPrefix . $this->compileValue($this->lib_e($key)); + } + + $seen =& $this->env->seenNames; + + if (!empty($seen[$key])) { + $this->throwError("infinite loop detected: $key"); + } + + $seen[$key] = true; + $out = $this->reduce($this->get($key, self::$defaultValue)); + $seen[$key] = false; + return $out; + case "list": + foreach ($value[2] as &$item) { + $item = $this->reduce($item, $forExpression); + } + return $value; + case "expression": + return $this->evaluate($value); + case "string": + foreach ($value[2] as &$part) { + if (is_array($part)) { + $strip = $part[0] == "variable"; + $part = $this->reduce($part); + if ($strip) $part = $this->lib_e($part); + } + } + return $value; + case "escape": + list(,$inner) = $value; + return $this->lib_e($this->reduce($inner)); + case "function": + $color = $this->funcToColor($value); + if ($color) return $color; + + list(, $name, $args) = $value; + if ($name == "%") $name = "_sprintf"; + $f = isset($this->libFunctions[$name]) ? + $this->libFunctions[$name] : array($this, 'lib_'.$name); + + if (is_callable($f)) { + if ($args[0] == 'list') + $args = self::compressList($args[2], $args[1]); + + $ret = call_user_func($f, $this->reduce($args, true), $this); + + if (is_null($ret)) { + return array("string", "", array( + $name, "(", $args, ")" + )); + } + + // convert to a typed value if the result is a php primitive + if (is_numeric($ret)) $ret = array('number', $ret, ""); + elseif (!is_array($ret)) $ret = array('keyword', $ret); + + return $ret; + } + + // plain function, reduce args + $value[2] = $this->reduce($value[2]); + return $value; + case "unary": + list(, $op, $exp) = $value; + $exp = $this->reduce($exp); + + if ($exp[0] == "number") { + switch ($op) { + case "+": + return $exp; + case "-": + $exp[1] *= -1; + return $exp; + } + } + return array("string", "", array($op, $exp)); + } + + if ($forExpression) { + switch ($value[0]) { + case "keyword": + if ($color = $this->coerceColor($value)) { + return $color; + } + break; + case "raw_color": + return $this->coerceColor($value); + } + } + + return $value; + } + + + // coerce a value for use in color operation + protected function coerceColor($value) { + switch($value[0]) { + case 'color': return $value; + case 'raw_color': + $c = array("color", 0, 0, 0); + $colorStr = substr($value[1], 1); + $num = hexdec($colorStr); + $width = strlen($colorStr) == 3 ? 16 : 256; + + for ($i = 3; $i > 0; $i--) { // 3 2 1 + $t = $num % $width; + $num /= $width; + + $c[$i] = $t * (256/$width) + $t * floor(16/$width); + } + + return $c; + case 'keyword': + $name = $value[1]; + if (isset(self::$cssColors[$name])) { + $rgba = explode(',', self::$cssColors[$name]); + + if(isset($rgba[3])) + return array('color', $rgba[0], $rgba[1], $rgba[2], $rgba[3]); + + return array('color', $rgba[0], $rgba[1], $rgba[2]); + } + return null; + } + } + + // make something string like into a string + protected function coerceString($value) { + switch ($value[0]) { + case "string": + return $value; + case "keyword": + return array("string", "", array($value[1])); + } + return null; + } + + // turn list of length 1 into value type + protected function flattenList($value) { + if ($value[0] == "list" && count($value[2]) == 1) { + return $this->flattenList($value[2][0]); + } + return $value; + } + + protected function toBool($a) { + if ($a) return self::$TRUE; + else return self::$FALSE; + } + + // evaluate an expression + protected function evaluate($exp) { + list(, $op, $left, $right, $whiteBefore, $whiteAfter) = $exp; + + $left = $this->reduce($left, true); + $right = $this->reduce($right, true); + + if ($leftColor = $this->coerceColor($left)) { + $left = $leftColor; + } + + if ($rightColor = $this->coerceColor($right)) { + $right = $rightColor; + } + + $ltype = $left[0]; + $rtype = $right[0]; + + // operators that work on all types + if ($op == "and") { + return $this->toBool($left == self::$TRUE && $right == self::$TRUE); + } + + if ($op == "=") { + return $this->toBool($this->eq($left, $right) ); + } + + if ($op == "+" && !is_null($str = $this->stringConcatenate($left, $right))) { + return $str; + } + + // type based operators + $fname = "op_${ltype}_${rtype}"; + if (is_callable(array($this, $fname))) { + $out = $this->$fname($op, $left, $right); + if (!is_null($out)) return $out; + } + + // make the expression look it did before being parsed + $paddedOp = $op; + if ($whiteBefore) $paddedOp = " " . $paddedOp; + if ($whiteAfter) $paddedOp .= " "; + + return array("string", "", array($left, $paddedOp, $right)); + } + + protected function stringConcatenate($left, $right) { + if ($strLeft = $this->coerceString($left)) { + if ($right[0] == "string") { + $right[1] = ""; + } + $strLeft[2][] = $right; + return $strLeft; + } + + if ($strRight = $this->coerceString($right)) { + array_unshift($strRight[2], $left); + return $strRight; + } + } + + + // make sure a color's components don't go out of bounds + protected function fixColor($c) { + foreach (range(1, 3) as $i) { + if ($c[$i] < 0) $c[$i] = 0; + if ($c[$i] > 255) $c[$i] = 255; + } + + return $c; + } + + protected function op_number_color($op, $lft, $rgt) { + if ($op == '+' || $op == '*') { + return $this->op_color_number($op, $rgt, $lft); + } + } + + protected function op_color_number($op, $lft, $rgt) { + if ($rgt[0] == '%') $rgt[1] /= 100; + + return $this->op_color_color($op, $lft, + array_fill(1, count($lft) - 1, $rgt[1])); + } + + protected function op_color_color($op, $left, $right) { + $out = array('color'); + $max = count($left) > count($right) ? count($left) : count($right); + foreach (range(1, $max - 1) as $i) { + $lval = isset($left[$i]) ? $left[$i] : 0; + $rval = isset($right[$i]) ? $right[$i] : 0; + switch ($op) { + case '+': + $out[] = $lval + $rval; + break; + case '-': + $out[] = $lval - $rval; + break; + case '*': + $out[] = $lval * $rval; + break; + case '%': + $out[] = $lval % $rval; + break; + case '/': + if ($rval == 0) $this->throwError("evaluate error: can't divide by zero"); + $out[] = $lval / $rval; + break; + default: + $this->throwError('evaluate error: color op number failed on op '.$op); + } + } + return $this->fixColor($out); + } + + function lib_red($color){ + $color = $this->coerceColor($color); + if (is_null($color)) { + $this->throwError('color expected for red()'); + } + + return $color[1]; + } + + function lib_green($color){ + $color = $this->coerceColor($color); + if (is_null($color)) { + $this->throwError('color expected for green()'); + } + + return $color[2]; + } + + function lib_blue($color){ + $color = $this->coerceColor($color); + if (is_null($color)) { + $this->throwError('color expected for blue()'); + } + + return $color[3]; + } + + + // operator on two numbers + protected function op_number_number($op, $left, $right) { + $unit = empty($left[2]) ? $right[2] : $left[2]; + + $value = 0; + switch ($op) { + case '+': + $value = $left[1] + $right[1]; + break; + case '*': + $value = $left[1] * $right[1]; + break; + case '-': + $value = $left[1] - $right[1]; + break; + case '%': + $value = $left[1] % $right[1]; + break; + case '/': + if ($right[1] == 0) $this->throwError('parse error: divide by zero'); + $value = $left[1] / $right[1]; + break; + case '<': + return $this->toBool($left[1] < $right[1]); + case '>': + return $this->toBool($left[1] > $right[1]); + case '>=': + return $this->toBool($left[1] >= $right[1]); + case '=<': + return $this->toBool($left[1] <= $right[1]); + default: + $this->throwError('parse error: unknown number operator: '.$op); + } + + return array("number", $value, $unit); + } + + + /* environment functions */ + + protected function makeOutputBlock($type, $selectors = null) { + $b = new stdclass; + $b->lines = array(); + $b->children = array(); + $b->selectors = $selectors; + $b->type = $type; + $b->parent = $this->scope; + return $b; + } + + // the state of execution + protected function pushEnv($block = null) { + $e = new stdclass; + $e->parent = $this->env; + $e->store = array(); + $e->block = $block; + + $this->env = $e; + return $e; + } + + // pop something off the stack + protected function popEnv() { + $old = $this->env; + $this->env = $this->env->parent; + return $old; + } + + // set something in the current env + protected function set($name, $value) { + $this->env->store[$name] = $value; + } + + + // get the highest occurrence entry for a name + protected function get($name, $default=null) { + $current = $this->env; + + $isArguments = $name == $this->vPrefix . 'arguments'; + while ($current) { + if ($isArguments && isset($current->arguments)) { + return array('list', ' ', $current->arguments); + } + + if (isset($current->store[$name])) + return $current->store[$name]; + else { + $current = isset($current->storeParent) ? + $current->storeParent : $current->parent; + } + } + + return $default; + } + + // inject array of unparsed strings into environment as variables + protected function injectVariables($args) { + $this->pushEnv(); + $parser = new lessc_parser($this, __METHOD__); + foreach ($args as $name => $strValue) { + if ($name{0} != '@') $name = '@'.$name; + $parser->count = 0; + $parser->buffer = (string)$strValue; + if (!$parser->propertyValue($value)) { + throw new Exception("failed to parse passed in variable $name: $strValue"); + } + + $this->set($name, $value); + } + } + + /** + * Initialize any static state, can initialize parser for a file + * $opts isn't used yet + */ + public function __construct($fname = null) { + if ($fname !== null) { + // used for deprecated parse method + $this->_parseFile = $fname; + } + } + + public function compile($string, $name = null) { + $locale = setlocale(LC_NUMERIC, 0); + setlocale(LC_NUMERIC, "C"); + + $this->parser = $this->makeParser($name); + $root = $this->parser->parse($string); + + $this->env = null; + $this->scope = null; + + $this->formatter = $this->newFormatter(); + + if (!empty($this->registeredVars)) { + $this->injectVariables($this->registeredVars); + } + + $this->sourceParser = $this->parser; // used for error messages + $this->compileBlock($root); + + ob_start(); + $this->formatter->block($this->scope); + $out = ob_get_clean(); + setlocale(LC_NUMERIC, $locale); + return $out; + } + + public function compileFile($fname, $outFname = null) { + if (!is_readable($fname)) { + throw new Exception('load error: failed to find '.$fname); + } + + $pi = pathinfo($fname); + + $oldImport = $this->importDir; + + $this->importDir = (array)$this->importDir; + $this->importDir[] = $pi['dirname'].'/'; + + $this->allParsedFiles = array(); + $this->addParsedFile($fname); + + $out = $this->compile(file_get_contents($fname), $fname); + + $this->importDir = $oldImport; + + if ($outFname !== null) { + return file_put_contents($outFname, $out); + } + + return $out; + } + + // compile only if changed input has changed or output doesn't exist + public function checkedCompile($in, $out) { + if (!is_file($out) || filemtime($in) > filemtime($out)) { + $this->compileFile($in, $out); + return true; + } + return false; + } + + /** + * Execute lessphp on a .less file or a lessphp cache structure + * + * The lessphp cache structure contains information about a specific + * less file having been parsed. It can be used as a hint for future + * calls to determine whether or not a rebuild is required. + * + * The cache structure contains two important keys that may be used + * externally: + * + * compiled: The final compiled CSS + * updated: The time (in seconds) the CSS was last compiled + * + * The cache structure is a plain-ol' PHP associative array and can + * be serialized and unserialized without a hitch. + * + * @param mixed $in Input + * @param bool $force Force rebuild? + * @return array lessphp cache structure + */ + public function cachedCompile($in, $force = false) { + // assume no root + $root = null; + + if (is_string($in)) { + $root = $in; + } elseif (is_array($in) and isset($in['root'])) { + if ($force or ! isset($in['files'])) { + // If we are forcing a recompile or if for some reason the + // structure does not contain any file information we should + // specify the root to trigger a rebuild. + $root = $in['root']; + } elseif (isset($in['files']) and is_array($in['files'])) { + foreach ($in['files'] as $fname => $ftime ) { + if (!file_exists($fname) or filemtime($fname) > $ftime) { + // One of the files we knew about previously has changed + // so we should look at our incoming root again. + $root = $in['root']; + break; + } + } + } + } else { + // TODO: Throw an exception? We got neither a string nor something + // that looks like a compatible lessphp cache structure. + return null; + } + + if ($root !== null) { + // If we have a root value which means we should rebuild. + $out = array(); + $out['root'] = $root; + $out['compiled'] = $this->compileFile($root); + $out['files'] = $this->allParsedFiles(); + $out['updated'] = time(); + return $out; + } else { + // No changes, pass back the structure + // we were given initially. + return $in; + } + + } + + // parse and compile buffer + // This is deprecated + public function parse($str = null, $initialVariables = null) { + if (is_array($str)) { + $initialVariables = $str; + $str = null; + } + + $oldVars = $this->registeredVars; + if ($initialVariables !== null) { + $this->setVariables($initialVariables); + } + + if ($str == null) { + if (empty($this->_parseFile)) { + throw new exception("nothing to parse"); + } + + $out = $this->compileFile($this->_parseFile); + } else { + $out = $this->compile($str); + } + + $this->registeredVars = $oldVars; + return $out; + } + + protected function makeParser($name) { + $parser = new lessc_parser($this, $name); + $parser->writeComments = $this->preserveComments; + + return $parser; + } + + public function setFormatter($name) { + $this->formatterName = $name; + } + + protected function newFormatter() { + $className = "lessc_formatter_lessjs"; + if (!empty($this->formatterName)) { + if (!is_string($this->formatterName)) + return $this->formatterName; + $className = "lessc_formatter_$this->formatterName"; + } + + return new $className; + } + + public function setPreserveComments($preserve) { + $this->preserveComments = $preserve; + } + + public function registerFunction($name, $func) { + $this->libFunctions[$name] = $func; + } + + public function unregisterFunction($name) { + unset($this->libFunctions[$name]); + } + + public function setVariables($variables) { + $this->registeredVars = array_merge($this->registeredVars, $variables); + } + + public function unsetVariable($name) { + unset($this->registeredVars[$name]); + } + + public function setImportDir($dirs) { + $this->importDir = (array)$dirs; + } + + public function addImportDir($dir) { + $this->importDir = (array)$this->importDir; + $this->importDir[] = $dir; + } + + public function allParsedFiles() { + return $this->allParsedFiles; + } + + protected function addParsedFile($file) { + $this->allParsedFiles[realpath($file)] = filemtime($file); + } + + /** + * Uses the current value of $this->count to show line and line number + */ + protected function throwError($msg = null) { + if ($this->sourceLoc >= 0) { + $this->sourceParser->throwError($msg, $this->sourceLoc); + } + throw new exception($msg); + } + + // compile file $in to file $out if $in is newer than $out + // returns true when it compiles, false otherwise + public static function ccompile($in, $out, $less = null) { + if ($less === null) { + $less = new self; + } + return $less->checkedCompile($in, $out); + } + + public static function cexecute($in, $force = false, $less = null) { + if ($less === null) { + $less = new self; + } + return $less->cachedCompile($in, $force); + } + + static protected $cssColors = array( + 'aliceblue' => '240,248,255', + 'antiquewhite' => '250,235,215', + 'aqua' => '0,255,255', + 'aquamarine' => '127,255,212', + 'azure' => '240,255,255', + 'beige' => '245,245,220', + 'bisque' => '255,228,196', + 'black' => '0,0,0', + 'blanchedalmond' => '255,235,205', + 'blue' => '0,0,255', + 'blueviolet' => '138,43,226', + 'brown' => '165,42,42', + 'burlywood' => '222,184,135', + 'cadetblue' => '95,158,160', + 'chartreuse' => '127,255,0', + 'chocolate' => '210,105,30', + 'coral' => '255,127,80', + 'cornflowerblue' => '100,149,237', + 'cornsilk' => '255,248,220', + 'crimson' => '220,20,60', + 'cyan' => '0,255,255', + 'darkblue' => '0,0,139', + 'darkcyan' => '0,139,139', + 'darkgoldenrod' => '184,134,11', + 'darkgray' => '169,169,169', + 'darkgreen' => '0,100,0', + 'darkgrey' => '169,169,169', + 'darkkhaki' => '189,183,107', + 'darkmagenta' => '139,0,139', + 'darkolivegreen' => '85,107,47', + 'darkorange' => '255,140,0', + 'darkorchid' => '153,50,204', + 'darkred' => '139,0,0', + 'darksalmon' => '233,150,122', + 'darkseagreen' => '143,188,143', + 'darkslateblue' => '72,61,139', + 'darkslategray' => '47,79,79', + 'darkslategrey' => '47,79,79', + 'darkturquoise' => '0,206,209', + 'darkviolet' => '148,0,211', + 'deeppink' => '255,20,147', + 'deepskyblue' => '0,191,255', + 'dimgray' => '105,105,105', + 'dimgrey' => '105,105,105', + 'dodgerblue' => '30,144,255', + 'firebrick' => '178,34,34', + 'floralwhite' => '255,250,240', + 'forestgreen' => '34,139,34', + 'fuchsia' => '255,0,255', + 'gainsboro' => '220,220,220', + 'ghostwhite' => '248,248,255', + 'gold' => '255,215,0', + 'goldenrod' => '218,165,32', + 'gray' => '128,128,128', + 'green' => '0,128,0', + 'greenyellow' => '173,255,47', + 'grey' => '128,128,128', + 'honeydew' => '240,255,240', + 'hotpink' => '255,105,180', + 'indianred' => '205,92,92', + 'indigo' => '75,0,130', + 'ivory' => '255,255,240', + 'khaki' => '240,230,140', + 'lavender' => '230,230,250', + 'lavenderblush' => '255,240,245', + 'lawngreen' => '124,252,0', + 'lemonchiffon' => '255,250,205', + 'lightblue' => '173,216,230', + 'lightcoral' => '240,128,128', + 'lightcyan' => '224,255,255', + 'lightgoldenrodyellow' => '250,250,210', + 'lightgray' => '211,211,211', + 'lightgreen' => '144,238,144', + 'lightgrey' => '211,211,211', + 'lightpink' => '255,182,193', + 'lightsalmon' => '255,160,122', + 'lightseagreen' => '32,178,170', + 'lightskyblue' => '135,206,250', + 'lightslategray' => '119,136,153', + 'lightslategrey' => '119,136,153', + 'lightsteelblue' => '176,196,222', + 'lightyellow' => '255,255,224', + 'lime' => '0,255,0', + 'limegreen' => '50,205,50', + 'linen' => '250,240,230', + 'magenta' => '255,0,255', + 'maroon' => '128,0,0', + 'mediumaquamarine' => '102,205,170', + 'mediumblue' => '0,0,205', + 'mediumorchid' => '186,85,211', + 'mediumpurple' => '147,112,219', + 'mediumseagreen' => '60,179,113', + 'mediumslateblue' => '123,104,238', + 'mediumspringgreen' => '0,250,154', + 'mediumturquoise' => '72,209,204', + 'mediumvioletred' => '199,21,133', + 'midnightblue' => '25,25,112', + 'mintcream' => '245,255,250', + 'mistyrose' => '255,228,225', + 'moccasin' => '255,228,181', + 'navajowhite' => '255,222,173', + 'navy' => '0,0,128', + 'oldlace' => '253,245,230', + 'olive' => '128,128,0', + 'olivedrab' => '107,142,35', + 'orange' => '255,165,0', + 'orangered' => '255,69,0', + 'orchid' => '218,112,214', + 'palegoldenrod' => '238,232,170', + 'palegreen' => '152,251,152', + 'paleturquoise' => '175,238,238', + 'palevioletred' => '219,112,147', + 'papayawhip' => '255,239,213', + 'peachpuff' => '255,218,185', + 'peru' => '205,133,63', + 'pink' => '255,192,203', + 'plum' => '221,160,221', + 'powderblue' => '176,224,230', + 'purple' => '128,0,128', + 'red' => '255,0,0', + 'rosybrown' => '188,143,143', + 'royalblue' => '65,105,225', + 'saddlebrown' => '139,69,19', + 'salmon' => '250,128,114', + 'sandybrown' => '244,164,96', + 'seagreen' => '46,139,87', + 'seashell' => '255,245,238', + 'sienna' => '160,82,45', + 'silver' => '192,192,192', + 'skyblue' => '135,206,235', + 'slateblue' => '106,90,205', + 'slategray' => '112,128,144', + 'slategrey' => '112,128,144', + 'snow' => '255,250,250', + 'springgreen' => '0,255,127', + 'steelblue' => '70,130,180', + 'tan' => '210,180,140', + 'teal' => '0,128,128', + 'thistle' => '216,191,216', + 'tomato' => '255,99,71', + 'transparent' => '0,0,0,0', + 'turquoise' => '64,224,208', + 'violet' => '238,130,238', + 'wheat' => '245,222,179', + 'white' => '255,255,255', + 'whitesmoke' => '245,245,245', + 'yellow' => '255,255,0', + 'yellowgreen' => '154,205,50' + ); +} + +// responsible for taking a string of LESS code and converting it into a +// syntax tree +class lessc_parser { + static protected $nextBlockId = 0; // used to uniquely identify blocks + + static protected $precedence = array( + '=<' => 0, + '>=' => 0, + '=' => 0, + '<' => 0, + '>' => 0, + + '+' => 1, + '-' => 1, + '*' => 2, + '/' => 2, + '%' => 2, + ); + + static protected $whitePattern; + static protected $commentMulti; + + static protected $commentSingle = "//"; + static protected $commentMultiLeft = "/*"; + static protected $commentMultiRight = "*/"; + + // regex string to match any of the operators + static protected $operatorString; + + // these properties will supress division unless it's inside parenthases + static protected $supressDivisionProps = + array('/border-radius$/i', '/^font$/i'); + + protected $blockDirectives = array("font-face", "keyframes", "page", "-moz-document"); + protected $lineDirectives = array("charset"); + + /** + * if we are in parens we can be more liberal with whitespace around + * operators because it must evaluate to a single value and thus is less + * ambiguous. + * + * Consider: + * property1: 10 -5; // is two numbers, 10 and -5 + * property2: (10 -5); // should evaluate to 5 + */ + protected $inParens = false; + + // caches preg escaped literals + static protected $literalCache = array(); + + public function __construct($lessc, $sourceName = null) { + $this->eatWhiteDefault = true; + // reference to less needed for vPrefix, mPrefix, and parentSelector + $this->lessc = $lessc; + + $this->sourceName = $sourceName; // name used for error messages + + $this->writeComments = false; + + if (!self::$operatorString) { + self::$operatorString = + '('.implode('|', array_map(array('lessc', 'preg_quote'), + array_keys(self::$precedence))).')'; + + $commentSingle = lessc::preg_quote(self::$commentSingle); + $commentMultiLeft = lessc::preg_quote(self::$commentMultiLeft); + $commentMultiRight = lessc::preg_quote(self::$commentMultiRight); + + self::$commentMulti = $commentMultiLeft.'.*?'.$commentMultiRight; + self::$whitePattern = '/'.$commentSingle.'[^\n]*\s*|('.self::$commentMulti.')\s*|\s+/Ais'; + } + } + + public function parse($buffer) { + $this->count = 0; + $this->line = 1; + + $this->env = null; // block stack + $this->buffer = $this->writeComments ? $buffer : $this->removeComments($buffer); + $this->pushSpecialBlock("root"); + $this->eatWhiteDefault = true; + $this->seenComments = array(); + + // trim whitespace on head + // if (preg_match('/^\s+/', $this->buffer, $m)) { + // $this->line += substr_count($m[0], "\n"); + // $this->buffer = ltrim($this->buffer); + // } + $this->whitespace(); + + // parse the entire file + $lastCount = $this->count; + while (false !== $this->parseChunk()); + + if ($this->count != strlen($this->buffer)) + $this->throwError(); + + // TODO report where the block was opened + if (!is_null($this->env->parent)) + throw new exception('parse error: unclosed block'); + + return $this->env; + } + + /** + * Parse a single chunk off the head of the buffer and append it to the + * current parse environment. + * Returns false when the buffer is empty, or when there is an error. + * + * This function is called repeatedly until the entire document is + * parsed. + * + * This parser is most similar to a recursive descent parser. Single + * functions represent discrete grammatical rules for the language, and + * they are able to capture the text that represents those rules. + * + * Consider the function lessc::keyword(). (all parse functions are + * structured the same) + * + * The function takes a single reference argument. When calling the + * function it will attempt to match a keyword on the head of the buffer. + * If it is successful, it will place the keyword in the referenced + * argument, advance the position in the buffer, and return true. If it + * fails then it won't advance the buffer and it will return false. + * + * All of these parse functions are powered by lessc::match(), which behaves + * the same way, but takes a literal regular expression. Sometimes it is + * more convenient to use match instead of creating a new function. + * + * Because of the format of the functions, to parse an entire string of + * grammatical rules, you can chain them together using &&. + * + * But, if some of the rules in the chain succeed before one fails, then + * the buffer position will be left at an invalid state. In order to + * avoid this, lessc::seek() is used to remember and set buffer positions. + * + * Before parsing a chain, use $s = $this->seek() to remember the current + * position into $s. Then if a chain fails, use $this->seek($s) to + * go back where we started. + */ + protected function parseChunk() { + if (empty($this->buffer)) return false; + $s = $this->seek(); + + // setting a property + if ($this->keyword($key) && $this->assign() && + $this->propertyValue($value, $key) && $this->end()) + { + $this->append(array('assign', $key, $value), $s); + return true; + } else { + $this->seek($s); + } + + + // look for special css blocks + if ($this->literal('@', false)) { + $this->count--; + + // media + if ($this->literal('@media')) { + if (($this->mediaQueryList($mediaQueries) || true) + && $this->literal('{')) + { + $media = $this->pushSpecialBlock("media"); + $media->queries = is_null($mediaQueries) ? array() : $mediaQueries; + return true; + } else { + $this->seek($s); + return false; + } + } + + if ($this->literal("@", false) && $this->keyword($dirName)) { + if ($this->isDirective($dirName, $this->blockDirectives)) { + if (($this->openString("{", $dirValue, null, array(";")) || true) && + $this->literal("{")) + { + $dir = $this->pushSpecialBlock("directive"); + $dir->name = $dirName; + if (isset($dirValue)) $dir->value = $dirValue; + return true; + } + } elseif ($this->isDirective($dirName, $this->lineDirectives)) { + if ($this->propertyValue($dirValue) && $this->end()) { + $this->append(array("directive", $dirName, $dirValue)); + return true; + } + } + } + + $this->seek($s); + } + + // setting a variable + if ($this->variable($var) && $this->assign() && + $this->propertyValue($value) && $this->end()) + { + $this->append(array('assign', $var, $value), $s); + return true; + } else { + $this->seek($s); + } + + if ($this->import($importValue)) { + $this->append($importValue, $s); + return true; + } + + // opening parametric mixin + if ($this->tag($tag, true) && $this->argumentDef($args, $isVararg) && + ($this->guards($guards) || true) && + $this->literal('{')) + { + $block = $this->pushBlock($this->fixTags(array($tag))); + $block->args = $args; + $block->isVararg = $isVararg; + if (!empty($guards)) $block->guards = $guards; + return true; + } else { + $this->seek($s); + } + + // opening a simple block + if ($this->tags($tags) && $this->literal('{')) { + $tags = $this->fixTags($tags); + $this->pushBlock($tags); + return true; + } else { + $this->seek($s); + } + + // closing a block + if ($this->literal('}', false)) { + try { + $block = $this->pop(); + } catch (exception $e) { + $this->seek($s); + $this->throwError($e->getMessage()); + } + + $hidden = false; + if (is_null($block->type)) { + $hidden = true; + if (!isset($block->args)) { + foreach ($block->tags as $tag) { + if (!is_string($tag) || $tag{0} != $this->lessc->mPrefix) { + $hidden = false; + break; + } + } + } + + foreach ($block->tags as $tag) { + if (is_string($tag)) { + $this->env->children[$tag][] = $block; + } + } + } + + if (!$hidden) { + $this->append(array('block', $block), $s); + } + + // this is done here so comments aren't bundled into he block that + // was just closed + $this->whitespace(); + return true; + } + + // mixin + if ($this->mixinTags($tags) && + ($this->argumentValues($argv) || true) && + ($this->keyword($suffix) || true) && $this->end()) + { + $tags = $this->fixTags($tags); + $this->append(array('mixin', $tags, $argv, $suffix), $s); + return true; + } else { + $this->seek($s); + } + + // spare ; + if ($this->literal(';')) return true; + + return false; // got nothing, throw error + } + + protected function isDirective($dirname, $directives) { + // TODO: cache pattern in parser + $pattern = implode("|", + array_map(array("lessc", "preg_quote"), $directives)); + $pattern = '/^(-[a-z-]+-)?(' . $pattern . ')$/i'; + + return preg_match($pattern, $dirname); + } + + protected function fixTags($tags) { + // move @ tags out of variable namespace + foreach ($tags as &$tag) { + if ($tag{0} == $this->lessc->vPrefix) + $tag[0] = $this->lessc->mPrefix; + } + return $tags; + } + + // a list of expressions + protected function expressionList(&$exps) { + $values = array(); + + while ($this->expression($exp)) { + $values[] = $exp; + } + + if (count($values) == 0) return false; + + $exps = lessc::compressList($values, ' '); + return true; + } + + /** + * Attempt to consume an expression. + * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code + */ + protected function expression(&$out) { + if ($this->value($lhs)) { + $out = $this->expHelper($lhs, 0); + + // look for / shorthand + if (!empty($this->env->supressedDivision)) { + unset($this->env->supressedDivision); + $s = $this->seek(); + if ($this->literal("/") && $this->value($rhs)) { + $out = array("list", "", + array($out, array("keyword", "/"), $rhs)); + } else { + $this->seek($s); + } + } + + return true; + } + return false; + } + + /** + * recursively parse infix equation with $lhs at precedence $minP + */ + protected function expHelper($lhs, $minP) { + $this->inExp = true; + $ss = $this->seek(); + + while (true) { + $whiteBefore = isset($this->buffer[$this->count - 1]) && + ctype_space($this->buffer[$this->count - 1]); + + // If there is whitespace before the operator, then we require + // whitespace after the operator for it to be an expression + $needWhite = $whiteBefore && !$this->inParens; + + if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) { + if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) { + foreach (self::$supressDivisionProps as $pattern) { + if (preg_match($pattern, $this->env->currentProperty)) { + $this->env->supressedDivision = true; + break 2; + } + } + } + + + $whiteAfter = isset($this->buffer[$this->count - 1]) && + ctype_space($this->buffer[$this->count - 1]); + + if (!$this->value($rhs)) break; + + // peek for next operator to see what to do with rhs + if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > self::$precedence[$m[1]]) { + $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]); + } + + $lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore, $whiteAfter); + $ss = $this->seek(); + + continue; + } + + break; + } + + $this->seek($ss); + + return $lhs; + } + + // consume a list of values for a property + public function propertyValue(&$value, $keyName = null) { + $values = array(); + + if ($keyName !== null) $this->env->currentProperty = $keyName; + + $s = null; + while ($this->expressionList($v)) { + $values[] = $v; + $s = $this->seek(); + if (!$this->literal(',')) break; + } + + if ($s) $this->seek($s); + + if ($keyName !== null) unset($this->env->currentProperty); + + if (count($values) == 0) return false; + + $value = lessc::compressList($values, ', '); + return true; + } + + protected function parenValue(&$out) { + $s = $this->seek(); + + // speed shortcut + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "(") { + return false; + } + + $inParens = $this->inParens; + if ($this->literal("(") && + ($this->inParens = true) && $this->expression($exp) && + $this->literal(")")) + { + $out = $exp; + $this->inParens = $inParens; + return true; + } else { + $this->inParens = $inParens; + $this->seek($s); + } + + return false; + } + + // a single value + protected function value(&$value) { + $s = $this->seek(); + + // speed shortcut + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "-") { + // negation + if ($this->literal("-", false) && + (($this->variable($inner) && $inner = array("variable", $inner)) || + $this->unit($inner) || + $this->parenValue($inner))) + { + $value = array("unary", "-", $inner); + return true; + } else { + $this->seek($s); + } + } + + if ($this->parenValue($value)) return true; + if ($this->unit($value)) return true; + if ($this->color($value)) return true; + if ($this->func($value)) return true; + if ($this->string($value)) return true; + + if ($this->keyword($word)) { + $value = array('keyword', $word); + return true; + } + + // try a variable + if ($this->variable($var)) { + $value = array('variable', $var); + return true; + } + + // unquote string (should this work on any type? + if ($this->literal("~") && $this->string($str)) { + $value = array("escape", $str); + return true; + } else { + $this->seek($s); + } + + // css hack: \0 + if ($this->literal('\\') && $this->match('([0-9]+)', $m)) { + $value = array('keyword', '\\'.$m[1]); + return true; + } else { + $this->seek($s); + } + + return false; + } + + // an import statement + protected function import(&$out) { + $s = $this->seek(); + if (!$this->literal('@import')) return false; + + // @import "something.css" media; + // @import url("something.css") media; + // @import url(something.css) media; + + if ($this->propertyValue($value)) { + $out = array("import", $value); + return true; + } + } + + protected function mediaQueryList(&$out) { + if ($this->genericList($list, "mediaQuery", ",", false)) { + $out = $list[2]; + return true; + } + return false; + } + + protected function mediaQuery(&$out) { + $s = $this->seek(); + + $expressions = null; + $parts = array(); + + if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->keyword($mediaType)) { + $prop = array("mediaType"); + if (isset($only)) $prop[] = "only"; + if (isset($not)) $prop[] = "not"; + $prop[] = $mediaType; + $parts[] = $prop; + } else { + $this->seek($s); + } + + + if (!empty($mediaType) && !$this->literal("and")) { + // ~ + } else { + $this->genericList($expressions, "mediaExpression", "and", false); + if (is_array($expressions)) $parts = array_merge($parts, $expressions[2]); + } + + if (count($parts) == 0) { + $this->seek($s); + return false; + } + + $out = $parts; + return true; + } + + protected function mediaExpression(&$out) { + $s = $this->seek(); + $value = null; + if ($this->literal("(") && + $this->keyword($feature) && + ($this->literal(":") && $this->expression($value) || true) && + $this->literal(")")) + { + $out = array("mediaExp", $feature); + if ($value) $out[] = $value; + return true; + } elseif ($this->variable($variable)) { + $out = array('variable', $variable); + return true; + } + + $this->seek($s); + return false; + } + + // an unbounded string stopped by $end + protected function openString($end, &$out, $nestingOpen=null, $rejectStrs = null) { + $oldWhite = $this->eatWhiteDefault; + $this->eatWhiteDefault = false; + + $stop = array("'", '"', "@{", $end); + $stop = array_map(array("lessc", "preg_quote"), $stop); + // $stop[] = self::$commentMulti; + + if (!is_null($rejectStrs)) { + $stop = array_merge($stop, $rejectStrs); + } + + $patt = '(.*?)('.implode("|", $stop).')'; + + $nestingLevel = 0; + + $content = array(); + while ($this->match($patt, $m, false)) { + if (!empty($m[1])) { + $content[] = $m[1]; + if ($nestingOpen) { + $nestingLevel += substr_count($m[1], $nestingOpen); + } + } + + $tok = $m[2]; + + $this->count-= strlen($tok); + if ($tok == $end) { + if ($nestingLevel == 0) { + break; + } else { + $nestingLevel--; + } + } + + if (($tok == "'" || $tok == '"') && $this->string($str)) { + $content[] = $str; + continue; + } + + if ($tok == "@{" && $this->interpolation($inter)) { + $content[] = $inter; + continue; + } + + if (!empty($rejectStrs) && in_array($tok, $rejectStrs)) { + $ount = null; + break; + } + + $content[] = $tok; + $this->count+= strlen($tok); + } + + $this->eatWhiteDefault = $oldWhite; + + if (count($content) == 0) return false; + + // trim the end + if (is_string(end($content))) { + $content[count($content) - 1] = rtrim(end($content)); + } + + $out = array("string", "", $content); + return true; + } + + protected function string(&$out) { + $s = $this->seek(); + if ($this->literal('"', false)) { + $delim = '"'; + } elseif ($this->literal("'", false)) { + $delim = "'"; + } else { + return false; + } + + $content = array(); + + // look for either ending delim , escape, or string interpolation + $patt = '([^\n]*?)(@\{|\\\\|' . + lessc::preg_quote($delim).')'; + + $oldWhite = $this->eatWhiteDefault; + $this->eatWhiteDefault = false; + + while ($this->match($patt, $m, false)) { + $content[] = $m[1]; + if ($m[2] == "@{") { + $this->count -= strlen($m[2]); + if ($this->interpolation($inter, false)) { + $content[] = $inter; + } else { + $this->count += strlen($m[2]); + $content[] = "@{"; // ignore it + } + } elseif ($m[2] == '\\') { + $content[] = $m[2]; + if ($this->literal($delim, false)) { + $content[] = $delim; + } + } else { + $this->count -= strlen($delim); + break; // delim + } + } + + $this->eatWhiteDefault = $oldWhite; + + if ($this->literal($delim)) { + $out = array("string", $delim, $content); + return true; + } + + $this->seek($s); + return false; + } + + protected function interpolation(&$out) { + $oldWhite = $this->eatWhiteDefault; + $this->eatWhiteDefault = true; + + $s = $this->seek(); + if ($this->literal("@{") && + $this->openString("}", $interp, null, array("'", '"', ";")) && + $this->literal("}", false)) + { + $out = array("interpolate", $interp); + $this->eatWhiteDefault = $oldWhite; + if ($this->eatWhiteDefault) $this->whitespace(); + return true; + } + + $this->eatWhiteDefault = $oldWhite; + $this->seek($s); + return false; + } + + protected function unit(&$unit) { + // speed shortcut + if (isset($this->buffer[$this->count])) { + $char = $this->buffer[$this->count]; + if (!ctype_digit($char) && $char != ".") return false; + } + + if ($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?', $m)) { + $unit = array("number", $m[1], empty($m[2]) ? "" : $m[2]); + return true; + } + return false; + } + + // a # color + protected function color(&$out) { + if ($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))', $m)) { + if (strlen($m[1]) > 7) { + $out = array("string", "", array($m[1])); + } else { + $out = array("raw_color", $m[1]); + } + return true; + } + + return false; + } + + // consume a list of property values delimited by ; and wrapped in () + protected function argumentValues(&$args, $delim = ',') { + $s = $this->seek(); + if (!$this->literal('(')) return false; + + $values = array(); + while (true) { + if ($this->expressionList($value)) $values[] = $value; + if (!$this->literal($delim)) break; + else { + if ($value == null) $values[] = null; + $value = null; + } + } + + if (!$this->literal(')')) { + $this->seek($s); + return false; + } + + $args = $values; + return true; + } + + // consume an argument definition list surrounded by () + // each argument is a variable name with optional value + // or at the end a ... or a variable named followed by ... + protected function argumentDef(&$args, &$isVararg, $delim = ',') { + $s = $this->seek(); + if (!$this->literal('(')) return false; + + $values = array(); + + $isVararg = false; + while (true) { + if ($this->literal("...")) { + $isVararg = true; + break; + } + + if ($this->variable($vname)) { + $arg = array("arg", $vname); + $ss = $this->seek(); + if ($this->assign() && $this->expressionList($value)) { + $arg[] = $value; + } else { + $this->seek($ss); + if ($this->literal("...")) { + $arg[0] = "rest"; + $isVararg = true; + } + } + $values[] = $arg; + if ($isVararg) break; + continue; + } + + if ($this->value($literal)) { + $values[] = array("lit", $literal); + } + + if (!$this->literal($delim)) break; + } + + if (!$this->literal(')')) { + $this->seek($s); + return false; + } + + $args = $values; + + return true; + } + + // consume a list of tags + // this accepts a hanging delimiter + protected function tags(&$tags, $simple = false, $delim = ',') { + $tags = array(); + while ($this->tag($tt, $simple)) { + $tags[] = $tt; + if (!$this->literal($delim)) break; + } + if (count($tags) == 0) return false; + + return true; + } + + // list of tags of specifying mixin path + // optionally separated by > (lazy, accepts extra >) + protected function mixinTags(&$tags) { + $s = $this->seek(); + $tags = array(); + while ($this->tag($tt, true)) { + $tags[] = $tt; + $this->literal(">"); + } + + if (count($tags) == 0) return false; + + return true; + } + + // a bracketed value (contained within in a tag definition) + protected function tagBracket(&$value) { + // speed shortcut + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") { + return false; + } + + $s = $this->seek(); + if ($this->literal('[') && $this->to(']', $c, true) && $this->literal(']', false)) { + $value = '['.$c.']'; + // whitespace? + if ($this->whitespace()) $value .= " "; + + // escape parent selector, (yuck) + $value = str_replace($this->lessc->parentSelector, "$&$", $value); + return true; + } + + $this->seek($s); + return false; + } + + protected function tagExpression(&$value) { + $s = $this->seek(); + if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) { + $value = array('exp', $exp); + return true; + } + + $this->seek($s); + return false; + } + + // a space separated list of selectors + protected function tag(&$tag, $simple = false) { + if ($simple) + $chars = '^@,:;{}\][>\(\) "\''; + else + $chars = '^@,;{}["\''; + + $s = $this->seek(); + + if (!$simple && $this->tagExpression($tag)) { + return true; + } + + $hasExpression = false; + $parts = array(); + while ($this->tagBracket($first)) $parts[] = $first; + + $oldWhite = $this->eatWhiteDefault; + $this->eatWhiteDefault = false; + + while (true) { + if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) { + $parts[] = $m[1]; + if ($simple) break; + + while ($this->tagBracket($brack)) { + $parts[] = $brack; + } + continue; + } + + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") { + if ($this->interpolation($interp)) { + $hasExpression = true; + $interp[2] = true; // don't unescape + $parts[] = $interp; + continue; + } + + if ($this->literal("@")) { + $parts[] = "@"; + continue; + } + } + + if ($this->unit($unit)) { // for keyframes + $parts[] = $unit[1]; + $parts[] = $unit[2]; + continue; + } + + break; + } + + $this->eatWhiteDefault = $oldWhite; + if (!$parts) { + $this->seek($s); + return false; + } + + if ($hasExpression) { + $tag = array("exp", array("string", "", $parts)); + } else { + $tag = trim(implode($parts)); + } + + $this->whitespace(); + return true; + } + + // a css function + protected function func(&$func) { + $s = $this->seek(); + + if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) { + $fname = $m[1]; + + $sPreArgs = $this->seek(); + + $args = array(); + while (true) { + $ss = $this->seek(); + // this ugly nonsense is for ie filter properties + if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) { + $args[] = array("string", "", array($name, "=", $value)); + } else { + $this->seek($ss); + if ($this->expressionList($value)) { + $args[] = $value; + } + } + + if (!$this->literal(',')) break; + } + $args = array('list', ',', $args); + + if ($this->literal(')')) { + $func = array('function', $fname, $args); + return true; + } elseif ($fname == 'url') { + // couldn't parse and in url? treat as string + $this->seek($sPreArgs); + if ($this->openString(")", $string) && $this->literal(")")) { + $func = array('function', $fname, $string); + return true; + } + } + } + + $this->seek($s); + return false; + } + + // consume a less variable + protected function variable(&$name) { + $s = $this->seek(); + if ($this->literal($this->lessc->vPrefix, false) && + ($this->variable($sub) || $this->keyword($name))) + { + if (!empty($sub)) { + $name = array('variable', $sub); + } else { + $name = $this->lessc->vPrefix.$name; + } + return true; + } + + $name = null; + $this->seek($s); + return false; + } + + /** + * Consume an assignment operator + * Can optionally take a name that will be set to the current property name + */ + protected function assign($name = null) { + if ($name) $this->currentProperty = $name; + return $this->literal(':') || $this->literal('='); + } + + // consume a keyword + protected function keyword(&$word) { + if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) { + $word = $m[1]; + return true; + } + return false; + } + + // consume an end of statement delimiter + protected function end() { + if ($this->literal(';')) { + return true; + } elseif ($this->count == strlen($this->buffer) || $this->buffer{$this->count} == '}') { + // if there is end of file or a closing block next then we don't need a ; + return true; + } + return false; + } + + protected function guards(&$guards) { + $s = $this->seek(); + + if (!$this->literal("when")) { + $this->seek($s); + return false; + } + + $guards = array(); + + while ($this->guardGroup($g)) { + $guards[] = $g; + if (!$this->literal(",")) break; + } + + if (count($guards) == 0) { + $guards = null; + $this->seek($s); + return false; + } + + return true; + } + + // a bunch of guards that are and'd together + // TODO rename to guardGroup + protected function guardGroup(&$guardGroup) { + $s = $this->seek(); + $guardGroup = array(); + while ($this->guard($guard)) { + $guardGroup[] = $guard; + if (!$this->literal("and")) break; + } + + if (count($guardGroup) == 0) { + $guardGroup = null; + $this->seek($s); + return false; + } + + return true; + } + + protected function guard(&$guard) { + $s = $this->seek(); + $negate = $this->literal("not"); + + if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) { + $guard = $exp; + if ($negate) $guard = array("negate", $guard); + return true; + } + + $this->seek($s); + return false; + } + + /* raw parsing functions */ + + protected function literal($what, $eatWhitespace = null) { + if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault; + + // shortcut on single letter + if (!isset($what[1]) && isset($this->buffer[$this->count])) { + if ($this->buffer[$this->count] == $what) { + if (!$eatWhitespace) { + $this->count++; + return true; + } + // goes below... + } else { + return false; + } + } + + if (!isset(self::$literalCache[$what])) { + self::$literalCache[$what] = lessc::preg_quote($what); + } + + return $this->match(self::$literalCache[$what], $m, $eatWhitespace); + } + + protected function genericList(&$out, $parseItem, $delim="", $flatten=true) { + $s = $this->seek(); + $items = array(); + while ($this->$parseItem($value)) { + $items[] = $value; + if ($delim) { + if (!$this->literal($delim)) break; + } + } + + if (count($items) == 0) { + $this->seek($s); + return false; + } + + if ($flatten && count($items) == 1) { + $out = $items[0]; + } else { + $out = array("list", $delim, $items); + } + + return true; + } + + + // advance counter to next occurrence of $what + // $until - don't include $what in advance + // $allowNewline, if string, will be used as valid char set + protected function to($what, &$out, $until = false, $allowNewline = false) { + if (is_string($allowNewline)) { + $validChars = $allowNewline; + } else { + $validChars = $allowNewline ? "." : "[^\n]"; + } + if (!$this->match('('.$validChars.'*?)'.lessc::preg_quote($what), $m, !$until)) return false; + if ($until) $this->count -= strlen($what); // give back $what + $out = $m[1]; + return true; + } + + // try to match something on head of buffer + protected function match($regex, &$out, $eatWhitespace = null) { + if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault; + + $r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais'; + if (preg_match($r, $this->buffer, $out, null, $this->count)) { + $this->count += strlen($out[0]); + if ($eatWhitespace && $this->writeComments) $this->whitespace(); + return true; + } + return false; + } + + // match some whitespace + protected function whitespace() { + if ($this->writeComments) { + $gotWhite = false; + while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) { + if (isset($m[1]) && empty($this->commentsSeen[$this->count])) { + $this->append(array("comment", $m[1])); + $this->commentsSeen[$this->count] = true; + } + $this->count += strlen($m[0]); + $gotWhite = true; + } + return $gotWhite; + } else { + $this->match("", $m); + return strlen($m[0]) > 0; + } + } + + // match something without consuming it + protected function peek($regex, &$out = null, $from=null) { + if (is_null($from)) $from = $this->count; + $r = '/'.$regex.'/Ais'; + $result = preg_match($r, $this->buffer, $out, null, $from); + + return $result; + } + + // seek to a spot in the buffer or return where we are on no argument + protected function seek($where = null) { + if ($where === null) return $this->count; + else $this->count = $where; + return true; + } + + /* misc functions */ + + public function throwError($msg = "parse error", $count = null) { + $count = is_null($count) ? $this->count : $count; + + $line = $this->line + + substr_count(substr($this->buffer, 0, $count), "\n"); + + if (!empty($this->sourceName)) { + $loc = "$this->sourceName on line $line"; + } else { + $loc = "line: $line"; + } + + // TODO this depends on $this->count + if ($this->peek("(.*?)(\n|$)", $m, $count)) { + throw new exception("$msg: failed at `$m[1]` $loc"); + } else { + throw new exception("$msg: $loc"); + } + } + + protected function pushBlock($selectors=null, $type=null) { + $b = new stdclass; + $b->parent = $this->env; + + $b->type = $type; + $b->id = self::$nextBlockId++; + + $b->isVararg = false; // TODO: kill me from here + $b->tags = $selectors; + + $b->props = array(); + $b->children = array(); + + $this->env = $b; + return $b; + } + + // push a block that doesn't multiply tags + protected function pushSpecialBlock($type) { + return $this->pushBlock(null, $type); + } + + // append a property to the current block + protected function append($prop, $pos = null) { + if ($pos !== null) $prop[-1] = $pos; + $this->env->props[] = $prop; + } + + // pop something off the stack + protected function pop() { + $old = $this->env; + $this->env = $this->env->parent; + return $old; + } + + // remove comments from $text + // todo: make it work for all functions, not just url + protected function removeComments($text) { + $look = array( + 'url(', '//', '/*', '"', "'" + ); + + $out = ''; + $min = null; + while (true) { + // find the next item + foreach ($look as $token) { + $pos = strpos($text, $token); + if ($pos !== false) { + if (!isset($min) || $pos < $min[1]) $min = array($token, $pos); + } + } + + if (is_null($min)) break; + + $count = $min[1]; + $skip = 0; + $newlines = 0; + switch ($min[0]) { + case 'url(': + if (preg_match('/url\(.*?\)/', $text, $m, 0, $count)) + $count += strlen($m[0]) - strlen($min[0]); + break; + case '"': + case "'": + if (preg_match('/'.$min[0].'.*?'.$min[0].'/', $text, $m, 0, $count)) + $count += strlen($m[0]) - 1; + break; + case '//': + $skip = strpos($text, "\n", $count); + if ($skip === false) $skip = strlen($text) - $count; + else $skip -= $count; + break; + case '/*': + if (preg_match('/\/\*.*?\*\//s', $text, $m, 0, $count)) { + $skip = strlen($m[0]); + $newlines = substr_count($m[0], "\n"); + } + break; + } + + if ($skip == 0) $count += strlen($min[0]); + + $out .= substr($text, 0, $count).str_repeat("\n", $newlines); + $text = substr($text, $count + $skip); + + $min = null; + } + + return $out.$text; + } + +} + +class lessc_formatter_classic { + public $indentChar = " "; + + public $break = "\n"; + public $open = " {"; + public $close = "}"; + public $selectorSeparator = ", "; + public $assignSeparator = ":"; + + public $openSingle = " { "; + public $closeSingle = " }"; + + public $disableSingle = false; + public $breakSelectors = false; + + public $compressColors = false; + + public function __construct() { + $this->indentLevel = 0; + } + + public function indentStr($n = 0) { + return str_repeat($this->indentChar, max($this->indentLevel + $n, 0)); + } + + public function property($name, $value) { + return $name . $this->assignSeparator . $value . ";"; + } + + protected function isEmpty($block) { + if (empty($block->lines)) { + foreach ($block->children as $child) { + if (!$this->isEmpty($child)) return false; + } + + return true; + } + return false; + } + + public function block($block) { + if ($this->isEmpty($block)) return; + + $inner = $pre = $this->indentStr(); + + $isSingle = !$this->disableSingle && + is_null($block->type) && count($block->lines) == 1; + + if (!empty($block->selectors)) { + $this->indentLevel++; + + if ($this->breakSelectors) { + $selectorSeparator = $this->selectorSeparator . $this->break . $pre; + } else { + $selectorSeparator = $this->selectorSeparator; + } + + echo $pre . + implode($selectorSeparator, $block->selectors); + if ($isSingle) { + echo $this->openSingle; + $inner = ""; + } else { + echo $this->open . $this->break; + $inner = $this->indentStr(); + } + + } + + if (!empty($block->lines)) { + $glue = $this->break.$inner; + echo $inner . implode($glue, $block->lines); + if (!$isSingle && !empty($block->children)) { + echo $this->break; + } + } + + foreach ($block->children as $child) { + $this->block($child); + } + + if (!empty($block->selectors)) { + if (!$isSingle && empty($block->children)) echo $this->break; + + if ($isSingle) { + echo $this->closeSingle . $this->break; + } else { + echo $pre . $this->close . $this->break; + } + + $this->indentLevel--; + } + } +} + +class lessc_formatter_compressed extends lessc_formatter_classic { + public $disableSingle = true; + public $open = "{"; + public $selectorSeparator = ","; + public $assignSeparator = ":"; + public $break = ""; + public $compressColors = true; + + public function indentStr($n = 0) { + return ""; + } +} + +class lessc_formatter_lessjs extends lessc_formatter_classic { + public $disableSingle = true; + public $breakSelectors = true; + public $assignSeparator = ": "; + public $selectorSeparator = ","; +} + + diff --git a/widgets/widgets.php b/widgets/widgets.php new file mode 100644 index 000000000..04704afd6 --- /dev/null +++ b/widgets/widgets.php @@ -0,0 +1,1042 @@ +create_css($style, $preset); + $css = preg_replace('#/\*.*?\*/#s', '', $css); + $css = preg_replace('/\s*([{}|:;,])\s+/', '$1', $css); + $css = preg_replace('/\s\s+(.*)/', '$1', $css); + $css = str_replace(';}', '}', $css); + + set_site_transient('origin_wcss:'.$key, $css, 86400); + } + + return $css; +} + +/** + * Class SiteOrigin_Panels_Widget + */ +abstract class SiteOrigin_Panels_Widget extends WP_Widget{ + public $form_args; + protected $demo; + protected $origin_id; + public $sub_widgets; + + private $styles; + + /** + * Create the widget + * + * @param string $name Name for the widget displayed on the configuration page. + * @param array $widget_options Optional Passed to wp_register_sidebar_widget() + * - description: shown on the configuration page + * - classname + * @param array $control_options Optional Passed to wp_register_widget_control() + * - width: required if more than 250px + * - height: currently not used but may be needed in the future + * @param array $form Form arguments. + * @param array $demo Values for the demo of the page builder widget. + * @internal param string $id_base + */ + function __construct($name, $widget_options = array(), $control_options = array(), $form = array(), $demo = array()){ + $id_base = str_replace('SiteOrigin_Panels_Widget_', '', get_class($this)); + $id_base = strtolower(str_replace('_', '-', $id_base)); + + parent::__construct('origin_'.$id_base, $name, $widget_options, $control_options); + $this->origin_id = $id_base; + + $this->form_args = $form; + $this->demo = $demo; + $this->styles = array(); + $this->sub_widgets = array(); + } + + /** + * Update the widget and save the new CSS. + * + * @param array $old + * @param array $new + * @return array + */ + function update($new, $old) { + + // We wont clear cache if this is a preview + if(!siteorigin_panels_is_preview()){ + // Remove the old CSS file + if(!empty($old['origin_style'])) { + list($style, $preset) = explode(':', $old['origin_style']); + $this->clear_css_cache($style, $preset); + } + + // Clear the cache for all sub widgets + if(!empty($this->sub_widgets)){ + global $wp_widget_factory; + foreach($this->sub_widgets as $id => $sub) { + if(empty($old['origin_style_'.$id])) continue; + $the_widget = $wp_widget_factory->widgets[$sub[1]]; + list($style, $preset) = explode(':', $old['origin_style_'.$id]); + + $the_widget->clear_css_cache($style, $preset); + } + } + + + + } + + foreach($this->form_args as $field_id => $field_args) { + if($field_args['type'] == 'checkbox') { + $new[$field_id] = !empty($new[$field_id]); + } + } + + return $new; + } + + /** + * Display the form for the widget. Auto generated from form array. + * + * @param array $instance + * @return string|void + */ + public function form($instance){ + + foreach($this->form_args as $field_id => $field_args) { + if(isset($field_args['default']) && !isset($instance[$field_id])) { + $instance[$field_id] = $field_args['default']; + } + if(!isset($instance[$field_id])) $instance[$field_id] = false; + + ?>

'; + + switch($field_args['type']) { + case 'text' : + ?>/> + + '.esc_html($field_args['description']).''; + + ?>

widget_options['default_style']) ? $this->widget_options['default_style'] : false; + } + + do_action('siteorigin_panels_widget_before_styles', $this, $instance); + + // Now, lets add the style options. + $styles = $this->get_styles(); + if( !empty( $styles ) ) { + ?> +

+ + +

+ sub_widgets as $id => $sub) { + global $wp_widget_factory; + $the_widget = $wp_widget_factory->widgets[$sub[1]]; + + if(!isset($instance['origin_style_'.$id])) $instance['origin_style_'.$id] = !empty($this->widget_options['default_style_'.$id]) ? $this->widget_options['default_style_'.$id] : false; + + ?> +

+ + +

+ form_args as $field_id => $field_args) { + if(isset($field_args['default']) && !isset($instance[$field_id])) { + $instance[$field_id] = $field_args['default']; + } + if(!isset($instance[$field_id])) $instance[$field_id] = false; + } + + // Filter the title + if(!empty($instance['title'])) { + $instance['title'] = apply_filters('widget_title', $instance['title'], $instance, $this->id_base); + } + + if(!empty($instance['origin_style'])) { + list($style, $preset) = explode(':', $instance['origin_style']); + $style = sanitize_file_name($style); + $preset = sanitize_file_name($preset); + + $data = $this->get_style_data($style); + $template = $data['Template']; + } + else { + $style = 'default'; + $preset = 'default'; + } + + if(empty($template)) $template = 'default'; + + $template_file = false; + $paths = $this->get_widget_paths(); + + foreach($paths as $path) { + if(file_exists($path.'/'.$this->origin_id.'/tpl/'.$template.'.php')) { + $template_file = $path.'/'.$this->origin_id.'/tpl/'.$template.'.php'; + break; + } + } + if(empty($template_file)) { + echo $args['before_widget']; + echo 'Template not found'; + echo $args['after_widget']; + return false; + } + + // Dynamically generate the CSS + if(!empty($instance['origin_style'])) { + $filename = $this->origin_id.'-'.$style.'-'.$preset; + if(siteorigin_panels_setting('inline-css')) { + static $inlined_css = array(); + if(empty($inlined_css[$filename])) { + $inlined_css[$filename] = true; + ?> get_class($this), + 'style' => $style, + 'preset' => $preset, + ), site_url('?action=origin_widgets_css') ), array(), SITEORIGIN_PANELS_VERSION ); + } + } + + if(method_exists($this, 'enqueue_scripts')) { + $this->enqueue_scripts(); + } + + $widget_classes = apply_filters('siteorigin_widgets_classes', array( + 'origin-widget', + 'origin-widget-'.$this->origin_id, + 'origin-widget-'.$this->origin_id.'-'. $style .'-' . $preset, + ), $instance); + + if(method_exists($this, 'widget_classes')) { + $widget_classes = $this->widget_classes(array( + 'origin-widget', + 'origin-widget-'.$this->origin_id, + 'origin-widget-'.$this->origin_id.'-'. $style .'-' . $preset, + ), $instance); + } + + echo $args['before_widget']; + echo '
'; + include $template_file; + echo '
'; + echo $args['after_widget']; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // Extra functions specific to a SiteOrigin widget. + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * A sub widget is a widget that's style is required by this widget + * + * @param $id + * @param $instance + */ + function sub_widget($id, $instance){ + $sub = $this->sub_widgets[$id]; + global $wp_widget_factory; + $the_widget = $wp_widget_factory->widgets[$sub[1]]; + $the_widget->widget(array('before_widget' => '', 'after_widget' => ''), $instance); + } + + /** + * Get the CSS for the given style and preset + * + * @param $style + * @param $preset + * @return string + */ + function create_css($style, $preset) { + $paths = $this->get_widget_paths(); + $style_file = false; + + // Find the file - exit if it can't be found. + foreach($paths as $path) { + if(file_exists($path.'/'.$this->origin_id.'/styles/'.$style.'.less')) { + $style_file = $path.'/'.$this->origin_id.'/styles/'.$style.'.less'; + break; + } + } + if(empty($style_file)) return ''; + + if( !class_exists('lessc') ) include plugin_dir_path(__FILE__) . 'lib/lessc.inc.php'; + + foreach($this->get_widget_folders() as $folder => $folder_url) { + $filename = rtrim($folder, '/') . '/' . $this->origin_id.'/styles/'.$style.'.less'; + if(file_exists($filename)) { + $less = file_get_contents($filename); + break; + } + } + // Add in the mixins + $less = str_replace( + '@import "../../../less/mixins";', + "\n\n".file_get_contents(plugin_dir_path(__FILE__).'less/mixins.less'), + $less + ); + + // Apply the preset variables to the LESS file + $presets = $this->get_style_presets($style); + if(!empty($presets[$preset]) && is_array($presets[$preset])){ + foreach($presets[$preset] as $k => $v) { + $less = preg_replace('/@'.preg_quote($k).':(.*);/', '@'.$k.': '.$v.';', $less); + } + } + + // Scope the CSS with the wrapper we'll be adding + $less = '.origin-widget.origin-widget-'.$this->origin_id.'-'.$style.'-'.$preset.' {' . $less . '}'; + $lc = new lessc(); + $lc->setPreserveComments(false); + + $lc->registerFunction('lumlighten', 'origin_widgets_less_lumlighten'); + $lc->registerFunction('lumdarken', 'origin_widgets_less_lumdarken'); + $lc->registerFunction('texture', 'origin_widgets_less_texture'); + $lc->registerFunction('widgetimage', 'origin_widgets_less_widgetimage'); + + // Create the CSS + return $lc->compile($less); + } + + /** + * Removes a CSS file + * + * @param $style + * @param $preset + */ + function clear_css_cache($style, $preset){ + $filename = $this->origin_id.'-'.$style.'-'.$preset; + delete_site_transient('origin_widgets_css_cache:'.$filename); + } + + /** + * Get all the paths where we'll look for widgets. + * + * @return array + */ + function get_widget_paths(){ + static $paths = array(); + + if(empty($paths)) { + $paths = array_keys($this->get_widget_folders()); + } + + return $paths; + } + + /** + * Get all the folders where we'll look for widgets + * + * @return mixed|void + */ + static function get_widget_folders(){ + static $folders = array(); + + if(empty($folders)) { + $folders = array( + get_stylesheet_directory().'/widgets' => get_stylesheet_directory_uri().'/widgets/widgets', + get_template_directory().'/widgets' => get_template_directory_uri().'/widgets', + plugin_dir_path(SITEORIGIN_PANELS_BASE_FILE).'widgets/widgets' => plugin_dir_url(SITEORIGIN_PANELS_BASE_FILE).'widgets/widgets', + ); + $folders = apply_filters('siteorigin_widget_folders', $folders); + } + + return $folders; + } + + /** + * Get all the folders where we'll look for widget images + * + * @return mixed|void + */ + static function get_image_folders(){ + static $folders = array(); + if(empty($folders)) { + $folders = array( + get_stylesheet_directory().'/widgets/img' => get_stylesheet_directory_uri().'/widgets/img', + get_template_directory().'/widgets/img' => get_template_directory_uri().'/widgets/img', + plugin_dir_path(SITEORIGIN_PANELS_BASE_FILE).'widgets/img' => plugin_dir_url(SITEORIGIN_PANELS_BASE_FILE).'widgets/img', + ); + $folders = apply_filters('siteorigin_widget_image_folders', $folders); + } + + return $folders; + } + + /** + * Get all the styles for this widget. + * + * @return array + */ + public function get_styles(){ + if( empty( $this->styles ) ) { + // We can add extra paths here + foreach($this->get_widget_paths() as $path) { + if(!is_dir($path)) continue; + + $files = glob($path.'/'.$this->origin_id.'/styles/*.less'); + if(!empty($files)) { + foreach(glob($path.'/'.$this->origin_id.'/styles/*.less') as $file) { + $p = pathinfo($file); + $this->styles[$p['filename']] = $this->get_style_data($p['filename']); + } + } + } + } + + return $this->styles; + } + + /** + * Get the presets for a given style + * + * @param $style_id + * @return mixed|void + */ + public function get_style_presets($style_id) { + + $presets = array(); + + foreach($this->get_widget_folders() as $folder => $folder_uri) { + $filename = rtrim($folder, '/') . '/' . $this->origin_id.'/presets/'.sanitize_file_name($style_id).'.php'; + + if(file_exists($filename)) { + // This file should register a filter that adds the presets + $new_presets = include($filename); + $presets = array_merge($presets, $new_presets); + } + } + + + return apply_filters('origin_widget_presets_'.$this->origin_id.'_'.$style_id, $presets); + } + + /** + * Get data for the style. + * + * @param $name + * @return array + */ + public function get_style_data($name) { + $paths = $this->get_widget_paths(); + + foreach($paths as $path) { + $filename = $path.'/'.$this->origin_id.'/styles/'.sanitize_file_name($name).'.less'; + if(!file_exists($filename)) continue; + + $data = get_file_data($filename, array( + 'Name' => 'Name', + 'Template' => 'Template', + 'Author' => 'Author', + 'Author URI' => 'Author URI', + ), 'origin_widget'); + return $data; + } + return false; + } + + /** + * Render a demo of the widget. + * + * @param array $args + */ + function render_demo($args = array()){ + $this->widget($args, $this->demo); + } + + /** + * Register a widget that we'll be using inside this widget. + * + * @param $id + * @param $name + * @param $class + */ + function add_sub_widget($id, $name, $class){ + $this->sub_widgets[$id] = array($name, $class); + } + + /** + * Add the fields required to query the posts. + */ + function add_post_query_fields(){ + // Add the posts type field + $post_types = get_post_types(array('public' => true)); + $post_types = array_values($post_types); + $this->form_args['query_post_type'] = array( + 'type' => 'select', + 'options' => $post_types, + 'label' => __('Post Type', 'siteorigin-panels') + ); + + // Add the posts per page field + $this->form_args['query_posts_per_page'] = array( + 'type' => 'number', + 'default' => 10, + 'label' => __('Posts Per Page', 'siteorigin-panels'), + ); + + $this->form_args['query_orderby'] = array( + 'type' => 'select', + 'label' => __('Order By', 'siteorigin-panels'), + 'options' => array( + 'none' => __('None', 'siteorigin-panels'), + 'ID' => __('Post ID', 'siteorigin-panels'), + 'author' => __('Author', 'siteorigin-panels'), + 'name' => __('Name', 'siteorigin-panels'), + 'name' => __('Name', 'siteorigin-panels'), + 'date' => __('Date', 'siteorigin-panels'), + 'modified' => __('Modified', 'siteorigin-panels'), + 'parent' => __('Parent', 'siteorigin-panels'), + 'rand' => __('Random', 'siteorigin-panels'), + 'comment_count' => __('Comment Count', 'siteorigin-panels'), + 'menu_order' => __('Menu Order', 'siteorigin-panels'), + ) + ); + + $this->form_args['query_order'] = array( + 'type' => 'select', + 'label' => __('Order', 'siteorigin-panels'), + 'options' => array( + 'ASC' => __('Ascending', 'siteorigin-panels'), + 'DESC' => __('Descending', 'siteorigin-panels'), + ) + ); + + $this->form_args['query_sticky'] = array( + 'type' => 'select', + 'label' => __('Sticky Posts', 'siteorigin-panels'), + 'options' => array( + '' => __('Default', 'siteorigin-panels'), + 'ignore' => __('Ignore Sticky', 'siteorigin-panels'), + 'exclude' => __('Exclude Sticky', 'siteorigin-panels'), + 'only' => __('Only Sticky', 'siteorigin-panels'), + ) + ); + + $this->form_args['query_additional'] = array( + 'type' => 'text', + 'label' => __('Additional Arguments', 'siteorigin-panels'), + 'description' => sprintf(__('Additional query arguments. See query_posts.', 'siteorigin-panels'), 'http://codex.wordpress.org/Function_Reference/query_posts'), + ); + } + + /** + * Get all the posts for the current query + * + * @param $instance + * @return WP_Query + */ + static function get_query_posts($instance) { + $query_args = array(); + foreach($instance as $k => $v){ + if(strpos($k, 'query_') === 0) { + $query_args[preg_replace('/query_/', '', $k, 1)] = $v; + } + } + $query = $query_args; + unset($query['additional']); + unset($query['sticky']); + + // Add the additional arguments + $query = wp_parse_args($query_args['additional'], $query); + + // Add the sticky posts if required + switch($query_args['sticky']){ + case 'ignore' : + $query['ignore_sticky_posts'] = 1; + break; + case 'only' : + $query['post__in'] = get_option( 'sticky_posts' ); + break; + case 'exclude' : + $query['post__not_in'] = get_option( 'sticky_posts' ); + break; + } + + // Add the current page + global $wp_query; + $query['paged'] = $wp_query->get('paged'); + + return new WP_Query($query); + } +} + +// All the standard bundled widgets + +/** + * A gallery widget + * + * Class SiteOrigin_Panels_Widgets_Gallery + */ +class SiteOrigin_Panels_Widgets_Gallery extends WP_Widget { + function __construct() { + parent::__construct( + 'siteorigin-panels-gallery', + __( 'Gallery (PB)', 'siteorigin-panels' ), + array( + 'description' => __( 'Displays a gallery.', 'siteorigin-panels' ), + ) + ); + } + + function widget( $args, $instance ) { + echo $args['before_widget']; + + $shortcode_attr = array(); + foreach($instance as $k => $v){ + if(empty($v)) continue; + $shortcode_attr[] = $k.'="'.esc_attr($v).'"'; + } + + echo do_shortcode('[gallery '.implode(' ', $shortcode_attr).']'); + + echo $args['after_widget']; + } + + function update( $new, $old ) { + return $new; + } + + function form( $instance ) { + global $_wp_additional_image_sizes; + + $types = apply_filters('siteorigin_panels_gallery_types', array()); + + $instance = wp_parse_args($instance, array( + 'ids' => '', + 'size' => apply_filters('siteorigin_panels_gallery_default_size', ''), + 'type' => apply_filters('siteorigin_panels_gallery_default_type', ''), + 'columns' => 3, + 'link' => '', + + )); + + ?> +

+ + + +

+

+ +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ + +

+ + __( 'Displays a simple image.', 'siteorigin-panels' ), + ) + ); + } + + /** + * @param array $args + * @param array $instance + */ + function widget( $args, $instance ) { + echo $args['before_widget']; + if(!empty($instance['href'])) echo ''; + echo ''; + if(!empty($instance['href'])) echo ''; + echo $args['after_widget']; + } + + function update($new, $old){ + $new = wp_parse_args($new, array( + 'src' => '', + 'href' => '', + )); + return $new; + } + + function form( $instance ) { + $instance = wp_parse_args($instance, array( + 'src' => '', + 'href' => '', + )); + + ?> +

+ + +

+

+ + +

+ __( 'Embeds a video.', 'siteorigin-panels' ), + ) + ); + } + + /** + * Display the video using + * + * @param array $args + * @param array $instance + */ + function widget( $args, $instance ) { + $embed = new WP_Embed(); + + if(!wp_script_is('fitvids')) + wp_enqueue_script('fitvids', plugin_dir_url(SITEORIGIN_PANELS_BASE_FILE).'widgets/js/jquery.fitvids.js', array('jquery'), SITEORIGIN_PANELS_VERSION); + + if(!wp_script_is('siteorigin-panels-embedded-video')) + wp_enqueue_script('siteorigin-panels-embedded-video', plugin_dir_url(SITEORIGIN_PANELS_BASE_FILE).'widgets/js/embedded-video.js', array('jquery', 'fitvids'), SITEORIGIN_PANELS_VERSION); + + echo $args['before_widget']; + ?>
run_shortcode( '[embed]' . $instance['video'] . '[/embed]' ) ?>
'', + ) ) + + ?> +

+ + +

+ __( 'A self hosted video player.', 'siteorigin-panels' ), + ) + ); + } + + function widget( $args, $instance ) { + if (empty($instance['url'])) return; + static $video_widget_id = 1; + + $instance = wp_parse_args($instance, array( + 'url' => '', + 'poster' => '', + 'skin' => 'siteorigin', + 'ratio' => 1.777, + 'autoplay' => false, + )); + + // Enqueue jPlayer scripts and intializer + wp_enqueue_script( 'siteorigin-panels-video-jplayer', plugin_dir_url(SITEORIGIN_PANELS_BASE_FILE).'video/jplayer/jquery.jplayer.min.js', array('jquery'), SITEORIGIN_PANELS_VERSION, true); + wp_enqueue_script( 'siteorigin-panels-video', plugin_dir_url(SITEORIGIN_PANELS_BASE_FILE).'video/panels.video.jquery.js', array('jquery'), SITEORIGIN_PANELS_VERSION, true); + + // Enqueue the SiteOrigin jPlayer skin + $skin = sanitize_file_name($instance['skin']); + wp_enqueue_style('siteorigin-panels-video-jplayer-skin', plugin_dir_url(SITEORIGIN_PANELS_BASE_FILE).'video/jplayer/skins/'.$skin.'/jplayer.'.$skin.'.css', array(), SITEORIGIN_PANELS_VERSION); + + $file = $instance['url']; + $poster = !empty($instance['poster']) ? $instance['poster'] : plugin_dir_url(SITEORIGIN_PANELS_BASE_FILE).'video/poster.jpg'; + $instance['ratio'] = floatval($instance['ratio']); + if(empty($instance['ratio'])) $instance['ratio'] = 1.777; + + echo $args['before_widget']; + + ?> + + '', + 'poster' => '', + 'skin' => 'siteorigin', + 'ratio' => 1.777, + 'autoplay' => false, + )); + + ?> +

+ + +

+

+ + + +

+

+ + +

+

+ + + +

+

+ +

+ '', + 'src' => '', + 'poster' => plugin_dir_url(SITEORIGIN_PANELS_BASE_FILE).'video/poster.jpg', + 'skin' => 'siteorigin', + 'ratio' => 1.777, + 'autoplay' => 0, + ), $atts ); + + if(!empty($instance['src'])) $instance['url'] = $instance['src']; + if(empty($instance['url'])) return; + + ob_start(); + the_widget('SiteOrigin_Panels_Widgets_Video', $instance); + return ob_get_clean(); + +} +add_shortcode('self_video', 'siteorigin_panels_video_shortcode'); + +/** + * Register the widgets. + */ +function siteorigin_panels_widgets_init(){ + register_widget('SiteOrigin_Panels_Widgets_Gallery'); + register_widget('SiteOrigin_Panels_Widgets_Image'); + register_widget('SiteOrigin_Panels_Widgets_EmbeddedVideo'); + register_widget('SiteOrigin_Panels_Widgets_Video'); +} +add_action('widgets_init', 'siteorigin_panels_widgets_init'); \ No newline at end of file diff --git a/widgets/widgets/animated-image/animated-image.php b/widgets/widgets/animated-image/animated-image.php new file mode 100644 index 000000000..8fb142b5c --- /dev/null +++ b/widgets/widgets/animated-image/animated-image.php @@ -0,0 +1,41 @@ + __('An image that animates in when it enters the screen.', 'siteorigin-panels'), + 'default_style' => 'simple', + ), + array(), + array( + 'image' => array( + 'type' => 'text', + 'label' => __('Image URL', 'siteorigin-panels'), + ), + 'animation' => array( + 'type' => 'select', + 'label' => __('Animation', 'siteorigin-panels'), + 'options' => array( + 'fade' => __('Fade In', 'siteorigin-panels'), + 'slide-up' => __('Slide Up', 'siteorigin-panels'), + 'slide-down' => __('Slide Down', 'siteorigin-panels'), + 'slide-left' => __('Slide Left', 'siteorigin-panels'), + 'slide-right' => __('Slide Right', 'siteorigin-panels'), + ) + ), + ) + ); + } + + function enqueue_scripts(){ + static $enqueued = false; + if(!$enqueued) { + wp_enqueue_script('siteorigin-widgets-'.$this->origin_id.'-onscreen', plugin_dir_url(__FILE__).'js/onscreen.js', array('jquery'), SITEORIGIN_PANELS_VERSION); + wp_enqueue_script('siteorigin-widgets-'.$this->origin_id, plugin_dir_url(__FILE__).'js/main.js', array('jquery'), SITEORIGIN_PANELS_VERSION); + $enqueued = true; + } + + } +} \ No newline at end of file diff --git a/widgets/widgets/animated-image/js/main.js b/widgets/widgets/animated-image/js/main.js new file mode 100644 index 000000000..543a3acd8 --- /dev/null +++ b/widgets/widgets/animated-image/js/main.js @@ -0,0 +1,42 @@ +jQuery(function($){ + var theInterval = setInterval(function(){ + // Check if any images that weren't visible, now are + $('.origin-widget-animated-image img').not('.animated').filter(':onScreen').each(function(){ + var $$ = $(this); + // TODO wait for images + if(!$$.get(0).complete) return; + + $$.addClass('animated'); + + setTimeout(function(){ + var a = $$.data('animation'); + + if(a == 'fade') { + $$.css('visibility', 'visible'); + $$.hide().fadeIn(750); + } + else { + var offset; + if(a == 'slide-up') offset = {top : 25, left: 0}; + else if(a == 'slide-down') offset = {top : -25, left: 0}; + else if(a == 'slide-left') offset = {top : 0, left: 25}; + else if(a == 'slide-right') offset = {top : 0, left: -25}; + + var $a = $$.clone().insertAfter($$).css({ + 'visibility' : 'visible', + 'opacity' : 0, + 'position' : 'absolute', + 'top' : $$.position().top + offset.top, + 'left' : $$.position().left + offset.left, + 'width' : $$.width(), + 'height' : $$.height() + }).animate({top: $$.position().top, left: $$.position().left, opacity: 1}, 750, function(){$(this).remove(); $$.css('visibility', 'visible');}); + } + }, 750); + } ); + + if($('.origin-widget-animated-image img').not('.animated').length == 0) { + clearInterval(theInterval); + } + }, 500); +}); \ No newline at end of file diff --git a/widgets/widgets/animated-image/js/onscreen.js b/widgets/widgets/animated-image/js/onscreen.js new file mode 100644 index 000000000..2999e1cc4 --- /dev/null +++ b/widgets/widgets/animated-image/js/onscreen.js @@ -0,0 +1,24 @@ +// onScreen jQuery plugin v0.2.1 +// (c) 2011 Ben Pickles +// +// http://benpickles.github.com/onScreen +// +// Released under MIT license. +;(function($) { + $.expr[":"].onScreen = function(elem) { + // The viewport position + var $window = $(window); + var viewport_top = $window.scrollTop(); + var viewport_height = $window.height(); + var viewport_bottom = viewport_top + viewport_height; + + // Element position + var $elem = $(elem); + var top = $elem.offset().top; + var height = $elem.height(); + var bottom = top + height; + + return (top >= viewport_top && bottom + 30 < viewport_bottom); + + } +})(jQuery); \ No newline at end of file diff --git a/widgets/widgets/animated-image/tpl/default.php b/widgets/widgets/animated-image/tpl/default.php new file mode 100644 index 000000000..65a6734e9 --- /dev/null +++ b/widgets/widgets/animated-image/tpl/default.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/widgets/widgets/button/button.php b/widgets/widgets/button/button.php new file mode 100644 index 000000000..6462e0932 --- /dev/null +++ b/widgets/widgets/button/button.php @@ -0,0 +1,43 @@ + __('A simple button', 'siteorigin-panels'), + 'default_style' => 'simple', + ), + array(), + array( + 'text' => array( + 'type' => 'text', + 'label' => __('Text', 'siteorigin-panels'), + ), + 'url' => array( + 'type' => 'text', + 'label' => __('Destination URL', 'siteorigin-panels'), + ), + 'new_window' => array( + 'type' => 'checkbox', + 'label' => __('Open In New Window', 'siteorigin-panels'), + ), + 'align' => array( + 'type' => 'select', + 'label' => __('Button Alignment', 'siteorigin-panels'), + 'options' => array( + 'left' => __('Left', 'siteorigin-panels'), + 'right' => __('Right', 'siteorigin-panels'), + 'center' => __('Center', 'siteorigin-panels'), + 'justify' => __('Justify', 'siteorigin-panels'), + ) + ), + ) + ); + } + + function widget_classes($classes, $instance) { + $classes[] = 'align-'.(empty($instance['align']) ? 'none' : $instance['align']); + return $classes; + } +} \ No newline at end of file diff --git a/widgets/widgets/button/presets/simple.php b/widgets/widgets/button/presets/simple.php new file mode 100644 index 000000000..3681fd1a9 --- /dev/null +++ b/widgets/widgets/button/presets/simple.php @@ -0,0 +1,54 @@ + array( + 'background_color' => '#F5F5F5', + 'text_darken' => '45%', + 'text_shadow' => '30%', + ), + + 'charcoal' => array( + 'background_color' => '#999999', + ), + + 'pink' => array( + 'background_color' => '#f6778c', + ), + + 'orange' => array( + 'background_color' => '#fece62', + 'text_darken' => '37.5%', + ), + + 'green' => array( + 'background_color' => '#b7d870', + 'text_darken' => '37.5%', + ), + + 'blue' => array( + 'background_color' => '#91dbf6', + 'text_darken' => '37.5%', + ), + + 'purple' => array( + 'background_color' => '#e5b2dd', + ), + + 'turquoise' => array( + 'background_color' => '#a2ebf1', + ), + + 'slate' => array( + 'background_color' => '#a5b7c3', + 'text_darken' => '45%', + 'text_shadow' => '15%', + 'inset_highlight' => '10%', + ), + + 'black' => array( + 'background_color' => '#333333', + 'text_darken' => '-75%', + 'text_shadow' => '-30%', + 'inset_highlight' => '17%', + ), +); diff --git a/widgets/widgets/button/styles/simple.css b/widgets/widgets/button/styles/simple.css new file mode 100644 index 000000000..84a13a6a6 --- /dev/null +++ b/widgets/widgets/button/styles/simple.css @@ -0,0 +1,71 @@ +/* +Name: Simple +Template: simple +Author: Greg Priday +Author URI: http://siteorigin.com/ +*/ +a { + display: inline-block; + padding: 10px 45px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + color: lumdarken(#f5f5f5, 35%); + font-size: 0.875em; + font-family: inherit; + font-weight: 500; + text-decoration: none; + text-shadow: 0 1px 0 lumlighten(#f5f5f5, 20%); + text-align: center; + -webkit-box-shadow: inset 0 1px 0 lumlighten(#f5f5f5, 10%), 0 1px 2px rgba(0,0,0,0.1); + -moz-box-shadow: inset 0 1px 0 lumlighten(#f5f5f5, 10%), 0 1px 2px rgba(0,0,0,0.1); + box-shadow: inset 0 1px 0 lumlighten(#f5f5f5, 10%), 0 1px 2px rgba(0,0,0,0.1); + background: #f5f5f5; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, lumdarken(#f5f5f5, 3.5)), color-stop(1, lumlighten(#f5f5f5, 3.5))); + background: -ms-linear-gradient(bottom, lumdarken(#f5f5f5, 3.5), lumlighten(#f5f5f5, 3.5)); + background: -moz-linear-gradient(center bottom, lumdarken(#f5f5f5, 3.5) 0%, lumlighten(#f5f5f5, 3.5) 100%); + background: -o-linear-gradient(lumlighten(#f5f5f5, 3.5), lumdarken(#f5f5f5, 3.5)); + border-top: solid 1px lumlighten(lumdarken(#f5f5f5, 18), 4); + border-left: solid 1px lumdarken(#f5f5f5, 18); + border-right: solid 1px lumdarken(#f5f5f5, 18); + border-bottom: solid 1px lumdarken(lumdarken(#f5f5f5, 18), 4); +} +a:hover { + background: lumlighten(#f5f5f5, 1.75%); + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, lumdarken(lumlighten(#f5f5f5, 1.75%), 3.5)), color-stop(1, lumlighten(lumlighten(#f5f5f5, 1.75%), 3.5))); + background: -ms-linear-gradient(bottom, lumdarken(lumlighten(#f5f5f5, 1.75%), 3.5), lumlighten(lumlighten(#f5f5f5, 1.75%), 3.5)); + background: -moz-linear-gradient(center bottom, lumdarken(lumlighten(#f5f5f5, 1.75%), 3.5) 0%, lumlighten(lumlighten(#f5f5f5, 1.75%), 3.5) 100%); + background: -o-linear-gradient(lumlighten(lumlighten(#f5f5f5, 1.75%), 3.5), lumdarken(lumlighten(#f5f5f5, 1.75%), 3.5)); + border-top: solid 1px lumlighten(lumdarken(lumlighten(#f5f5f5, 1.75%), 18), 4); + border-left: solid 1px lumdarken(lumlighten(#f5f5f5, 1.75%), 18); + border-right: solid 1px lumdarken(lumlighten(#f5f5f5, 1.75%), 18); + border-bottom: solid 1px lumdarken(lumdarken(lumlighten(#f5f5f5, 1.75%), 18), 4); +} +a:active { + background: lumdarken(#f5f5f5, 1.75); + background: -webkit-gradient(linear, left bottom, left top, color-stop(0, lumdarken(lumdarken(#f5f5f5, 1.75), -2)), color-stop(1, lumlighten(lumdarken(#f5f5f5, 1.75), -2))); + background: -ms-linear-gradient(bottom, lumdarken(lumdarken(#f5f5f5, 1.75), -2), lumlighten(lumdarken(#f5f5f5, 1.75), -2)); + background: -moz-linear-gradient(center bottom, lumdarken(lumdarken(#f5f5f5, 1.75), -2) 0%, lumlighten(lumdarken(#f5f5f5, 1.75), -2) 100%); + background: -o-linear-gradient(lumlighten(lumdarken(#f5f5f5, 1.75), -2), lumdarken(lumdarken(#f5f5f5, 1.75), -2)); + border-top: solid 1px lumlighten(lumdarken(lumdarken(#f5f5f5, 1.75), 18), 4); + border-left: solid 1px lumdarken(lumdarken(#f5f5f5, 1.75), 18); + border-right: solid 1px lumdarken(lumdarken(#f5f5f5, 1.75), 18); + border-bottom: solid 1px lumdarken(lumdarken(lumdarken(#f5f5f5, 1.75), 18), 4); + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + padding-top: 11px; + padding-bottom: 9px; +} +.align-left { + text-align: left; +} +.align-right { + text-align: right; +} +.align-center { + text-align: center; +} +.align-justify a { + display: block; +} diff --git a/widgets/widgets/button/styles/simple.less b/widgets/widgets/button/styles/simple.less new file mode 100644 index 000000000..9894bd409 --- /dev/null +++ b/widgets/widgets/button/styles/simple.less @@ -0,0 +1,78 @@ +/* +Name: Simple +Template: simple +Author: Greg Priday +Author URI: http://siteorigin.com/ +*/ + +@background_color: #F5F5F5; + +@text_darken: 35%; +@inset_highlight: 10%; +@drop_shadow: 0.1; +@text_shadow: 20%; + +@rounded: 4px; +@font_size: 0.875em; + +@padding_top: 10px; +@padding_sides: 45px; + +@font_family: inherit; +@font_weight: 500; + +@import "../../../less/mixins"; + +& { + + a { + display:inline-block; + padding: @padding_top @padding_sides; + + .rounded(@rounded); + + color: lumdarken(@background_color, @text_darken); + + font-size: @font_size; + font-family: @font_family; + font-weight: @font_weight; + text-decoration:none; + + text-shadow: 0 1px 0 lumlighten(@background_color, @text_shadow); + text-align: center; + + @inset_highlight_color: lumlighten(@background_color, @inset_highlight); + .box-shadow(~"inset 0 1px 0 @{inset_highlight_color}, 0 1px 2px rgba(0,0,0,@{drop_shadow})"); + .button-style(@background_color); + + &:hover { + .button-style( lumlighten(@background_color, 1.75%) ); + } + + &:active { + .button-style(lumdarken(@background_color, 1.75), -2); + .box-shadow(none); + padding-top: @padding_top + 1; + padding-bottom: @padding_top - 1; + } + } + + &.align-left { + text-align: left; + } + + &.align-right { + text-align: right; + } + + &.align-center { + text-align: center; + } + + &.align-justify { + a { + display: block; + } + } + +} \ No newline at end of file diff --git a/widgets/widgets/button/tpl/simple.php b/widgets/widgets/button/tpl/simple.php new file mode 100644 index 000000000..e30208566 --- /dev/null +++ b/widgets/widgets/button/tpl/simple.php @@ -0,0 +1,3 @@ +> + + \ No newline at end of file diff --git a/widgets/widgets/call-to-action/call-to-action.php b/widgets/widgets/call-to-action/call-to-action.php new file mode 100644 index 000000000..310406ae4 --- /dev/null +++ b/widgets/widgets/call-to-action/call-to-action.php @@ -0,0 +1,39 @@ + __('A Call to Action block', 'siteorigin-panels'), + 'default_style' => 'simple', + ), + array(), + array( + 'title' => array( + 'type' => 'text', + 'label' => __('Title', 'siteorigin-panels'), + ), + 'subtitle' => array( + 'type' => 'text', + 'label' => __('Sub Title', 'siteorigin-panels'), + ), + 'button_text' => array( + 'type' => 'text', + 'label' => __('Button Text', 'siteorigin-panels'), + ), + 'button_url' => array( + 'type' => 'text', + 'label' => __('Button URL', 'siteorigin-panels'), + ), + 'button_new_window' => array( + 'type' => 'checkbox', + 'label' => __('Open In New Window', 'siteorigin-panels'), + ), + ) + ); + + // We need the button style + $this->add_sub_widget('button', __('Button', 'siteorigin-panels'), 'SiteOrigin_Panels_Widget_Button'); + } +} \ No newline at end of file diff --git a/widgets/widgets/call-to-action/presets/simple.php b/widgets/widgets/call-to-action/presets/simple.php new file mode 100644 index 000000000..2a3b50e71 --- /dev/null +++ b/widgets/widgets/call-to-action/presets/simple.php @@ -0,0 +1,24 @@ + array( + 'texture' => 'light-dashed', + 'title_text_color' => '#333333', + 'subtitle_text_color' => '#555555', + ), + + 'dark_dashed' => array( + 'texture' => 'dark-dashed', + 'title_text_color' => '#FFFFFF', + 'subtitle_text_color' => '#CCCCCC', + ), + + 'clean' => array( + 'background_color' => '#FCFCFC', + 'texture' => 'none', + 'title_text_color' => '#333333', + 'subtitle_text_color' => '#555555', + 'rounding' => '0px', + 'borders' => '1px solid #D0D0D0', + ), +); diff --git a/widgets/widgets/call-to-action/styles/simple.less b/widgets/widgets/call-to-action/styles/simple.less new file mode 100644 index 000000000..88740167f --- /dev/null +++ b/widgets/widgets/call-to-action/styles/simple.less @@ -0,0 +1,70 @@ +/* +Name: Simple +Template: simple +Author: Greg Priday +Author URI: http://siteorigin.com/ +*/ + +@import "../../../less/mixins"; + +/* Everything for the background */ + +@background_color: #F6F6F6; +@padding: 2em; +@rounding: 4px; +@shadow: 0.1; +@borders: 1px solid #E0E0E0; + +/* Everything for the Title */ + +@title_text_color: #EFEFEF; +@title_font_weight: auto; +@subtitle_text_color: #CCCCCC; +@subtitle_font_weight: auto; +@texture: dark-dashed; + +& { + .clearfix(); + padding: @padding; + position: relative; + .rounded(@rounding); + + background: texture(@texture, @background_color); + .box-shadow(0 1px 2px rgba(0,0,0,@shadow)); + border: @borders; + + .title { + line-height: 1.6em; + margin: 0; + color: @title_text_color; + font-weight: @title_font_weight; + } + + .subtitle { + line-height: 1.25em; + margin: 0; + color: @subtitle_text_color; + font-weight: @subtitle_font_weight; + } + + .origin-widget-button { + position: absolute; + display: block; + top: 50%; + right: 2em; + margin-top: -22px; + } +} + +@media (max-width:680px) { + & { + .origin-widget-button { + position: static; + margin-top: 2em; + + a { + display: block; + } + } + } +} \ No newline at end of file diff --git a/widgets/widgets/call-to-action/tpl/simple.php b/widgets/widgets/call-to-action/tpl/simple.php new file mode 100644 index 000000000..d34737582 --- /dev/null +++ b/widgets/widgets/call-to-action/tpl/simple.php @@ -0,0 +1,3 @@ +

+
+sub_widget('button', array('text' => $instance['button_text'], 'url' => $instance['button_url'], 'new_window' => $instance['button_new_window'], 'origin_style' => $instance['origin_style_button'])) ?> \ No newline at end of file diff --git a/widgets/widgets/list/list.php b/widgets/widgets/list/list.php new file mode 100644 index 000000000..25691b512 --- /dev/null +++ b/widgets/widgets/list/list.php @@ -0,0 +1,35 @@ + __('Displays a bullet list of elements', 'siteorigin-panels'), + 'default_style' => 'simple', + ), + array(), + array( + 'title' => array( + 'type' => 'text', + 'label' => __('Title', 'siteorigin-panels'), + ), + 'text' => array( + 'type' => 'textarea', + 'label' => __('Text', 'siteorigin-panels'), + 'description' => __('Start each new point with an asterisk (*)', 'siteorigin-panels'), + ), + ) + ); + } + + static function create_list($text){ + // Add the list items + $text = preg_replace( "/\*+(.*)?/i", "
  • $1
", $text ); + $text = preg_replace( "/(\<\/ul\>\n(.*)\*)+/", "", $text ); + $text = wpautop( $text ); + + // Return sanitized version of the list + return wp_kses_post($text); + } +} \ No newline at end of file diff --git a/widgets/widgets/list/presets/simple.php b/widgets/widgets/list/presets/simple.php new file mode 100644 index 000000000..1ac61fa03 --- /dev/null +++ b/widgets/widgets/list/presets/simple.php @@ -0,0 +1,39 @@ + array( + 'image' => 'charcoal', + ), + + 'pink' => array( + 'image' => 'pink', + ), + + 'orange' => array( + 'image' => 'orange', + ), + + 'green' => array( + 'image' => 'green', + ), + + 'blue' => array( + 'image' => 'blue', + ), + + 'purple' => array( + 'image' => 'purple', + ), + + 'turquoise' => array( + 'image' => 'turquoise', + ), + + 'slate' => array( + 'image' => 'slate', + ), + + 'black' => array( + 'image' => 'black', + ), +); diff --git a/widgets/widgets/list/styles/simple.less b/widgets/widgets/list/styles/simple.less new file mode 100644 index 000000000..67f6f7b14 --- /dev/null +++ b/widgets/widgets/list/styles/simple.less @@ -0,0 +1,23 @@ +/* +Name: Simple +Template: simple +Author: Greg Priday +Author URI: http://siteorigin.com/ +*/ + +@import "../../../less/mixins"; + +@image: white; + +ul { + margin: 0; + padding: 0; + + li { + @image_url: 'checks/@{image}.png'; + list-style: url(widgetimage(@image_url)); + line-height: 2em; + margin-left: 24px; + padding-left: 6px; + } +} \ No newline at end of file diff --git a/widgets/widgets/list/tpl/simple.php b/widgets/widgets/list/tpl/simple.php new file mode 100644 index 000000000..e8d448622 --- /dev/null +++ b/widgets/widgets/list/tpl/simple.php @@ -0,0 +1,5 @@ +create_list($instance['text']); \ No newline at end of file diff --git a/widgets/widgets/price-box/presets/simple.php b/widgets/widgets/price-box/presets/simple.php new file mode 100644 index 000000000..8410669a8 --- /dev/null +++ b/widgets/widgets/price-box/presets/simple.php @@ -0,0 +1,8 @@ + array( + 'background_color' => '#FCFCFC', + 'borders' => '1px solid #D0D0D0', + ), +); diff --git a/widgets/widgets/price-box/price-box.php b/widgets/widgets/price-box/price-box.php new file mode 100644 index 000000000..20435d75b --- /dev/null +++ b/widgets/widgets/price-box/price-box.php @@ -0,0 +1,52 @@ + __('Displays a bullet list of elements', 'siteorigin-panels'), + 'default_style' => 'simple', + ), + array(), + array( + 'title' => array( + 'type' => 'text', + 'label' => __('Title', 'siteorigin-panels'), + ), + 'price' => array( + 'type' => 'text', + 'label' => __('Price', 'siteorigin-panels'), + ), + 'per' => array( + 'type' => 'text', + 'label' => __('Per', 'siteorigin-panels'), + ), + 'information' => array( + 'type' => 'text', + 'label' => __('Information Text', 'siteorigin-panels'), + ), + 'features' => array( + 'type' => 'textarea', + 'label' => __('Features Text', 'siteorigin-panels'), + 'description' => __('Start each new point with an asterisk (*)', 'siteorigin-panels'), + ), + 'button_text' => array( + 'type' => 'text', + 'label' => __('Button Text', 'siteorigin-panels'), + ), + 'button_url' => array( + 'type' => 'text', + 'label' => __('Button URL', 'siteorigin-panels'), + ), + 'button_new_window' => array( + 'type' => 'checkbox', + 'label' => __('Open In New Window', 'siteorigin-panels'), + ), + ) + ); + + $this->add_sub_widget('button', __('Button', 'siteorigin-panels'), 'SiteOrigin_Panels_Widget_Button'); + $this->add_sub_widget('list', __('Feature List', 'siteorigin-panels'), 'SiteOrigin_Panels_Widget_List'); + } +} \ No newline at end of file diff --git a/widgets/widgets/price-box/styles/simple.less b/widgets/widgets/price-box/styles/simple.less new file mode 100644 index 000000000..a88e0d1dc --- /dev/null +++ b/widgets/widgets/price-box/styles/simple.less @@ -0,0 +1,38 @@ +/* +Name: Simple +Template: simple +Author: Greg Priday +Author URI: http://siteorigin.com/ +*/ + +@import "../../../less/mixins"; +@borders: 1px solid #E0E0E0; +@background_color: #FCFCFC; +@box_shadow: 0 1px 2px rgba(0,0,0,0.1); + +& { + padding: 25px 20px; + border: @borders; + background: @background_color; + .box-shadow(@box_shadow); + + h2 { + line-height: 1; + margin: 0 0 0.25em 0; + text-align: center; + } + + h4 { + line-height: 1; + margin: 0 0 1em 0; + text-align: center; + + span { + font-size: 0.6em; + } + } + + .origin-widget-button { + margin-top: 30px; + } +} \ No newline at end of file diff --git a/widgets/widgets/price-box/tpl/simple.php b/widgets/widgets/price-box/tpl/simple.php new file mode 100644 index 000000000..cf10bdc12 --- /dev/null +++ b/widgets/widgets/price-box/tpl/simple.php @@ -0,0 +1,16 @@ +

+

/

+

+ +sub_widget('list', array('title' => '', 'text' => $instance['features'], 'origin_style' => $instance['origin_style_list'])); +$this->sub_widget('button', array( + 'text' => $instance['button_text'], + 'url' => $instance['button_url'], + 'align' => 'center', + 'origin_style' => $instance['origin_style_button'], + 'new_window' => !empty($instance['button_new_window']) +)); + +?> \ No newline at end of file diff --git a/widgets/widgets/testimonial/presets/simple.php b/widgets/widgets/testimonial/presets/simple.php new file mode 100644 index 000000000..7de577889 --- /dev/null +++ b/widgets/widgets/testimonial/presets/simple.php @@ -0,0 +1,6 @@ + array( + ), +); diff --git a/widgets/widgets/testimonial/styles/simple.less b/widgets/widgets/testimonial/styles/simple.less new file mode 100644 index 000000000..923742383 --- /dev/null +++ b/widgets/widgets/testimonial/styles/simple.less @@ -0,0 +1,61 @@ +/* +Name: Simple +Template: simple +Author: Greg Priday +Author URI: http://siteorigin.com/ +*/ + +@import "../../../less/mixins"; + +@borders: 1px solid #D0D0D0; +@background_color: #FCFCFC; + +& { + background: @background_color; + border: @borders; + padding: 20px; + .clearfix(); + .box-shadow(0 1px 2px rgba(0,0,0,0.1)); + + h5.testimonial-name { + margin: 1em 0 5px 0; + line-height: 1; + color: #333; + clear: none; + + a { + color: inherit; + } + } + + small.testimonial-location { + font-size: 0.9em; + display: block; + margin: 0; + line-height: 1; + color: #999; + clear: none; + } + + .text { + margin-left: 80px; + font-size: 0.95em; + + p:first-child { + margin-top: 0; + } + } + + .testimonial-image-wrapper { + .rounded(4px); + float: left; + width: 60px; + height: 60px; + overflow: hidden; + background: #333; + img { + width: 100%; + height: auto; + } + } +} \ No newline at end of file diff --git a/widgets/widgets/testimonial/testimonial.php b/widgets/widgets/testimonial/testimonial.php new file mode 100644 index 000000000..8f72f0b91 --- /dev/null +++ b/widgets/widgets/testimonial/testimonial.php @@ -0,0 +1,40 @@ + __('Displays a bullet list of elements', 'siteorigin-panels'), + 'default_style' => 'simple', + ), + array(), + array( + 'name' => array( + 'type' => 'text', + 'label' => __('Name', 'siteorigin-panels'), + ), + 'location' => array( + 'type' => 'text', + 'label' => __('Location', 'siteorigin-panels'), + ), + 'image' => array( + 'type' => 'text', + 'label' => __('Image', 'siteorigin-panels'), + ), + 'text' => array( + 'type' => 'textarea', + 'label' => __('Text', 'siteorigin-panels'), + ), + 'url' => array( + 'type' => 'text', + 'label' => __('URL', 'siteorigin-panels'), + ), + 'new_window' => array( + 'type' => 'checkbox', + 'label' => __('Open In New Window', 'siteorigin-panels'), + ), + ) + ); + } +} \ No newline at end of file diff --git a/widgets/widgets/testimonial/tpl/simple.php b/widgets/widgets/testimonial/tpl/simple.php new file mode 100644 index 000000000..31a9203b5 --- /dev/null +++ b/widgets/widgets/testimonial/tpl/simple.php @@ -0,0 +1,13 @@ +
+ +
+ +
+ +
+ + + +
+ +
\ No newline at end of file