diff --git a/.travis.yml b/.travis.yml index c06a92f0..f7bc70f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ cache: before_script: - | - if [[ "$WP_TRAVISCI" == "travis:phpunit" ]] ; then + if [[ "$WP_TRAVISCI" == "travis:phpunit" ]] || [[ "$WP_TRAVISCI" == "travis:js-tests" ]]; then bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION fi - | diff --git a/Gruntfile.js b/Gruntfile.js index 18d0b464..89faa00c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -2,7 +2,20 @@ module.exports = function( grunt ) { 'use strict'; var remapify = require('remapify'); - var banner = '/**\n * <%= pkg.homepage %>\n * Copyright (c) <%= grunt.template.today("yyyy") %>\n * This file is generated automatically. Do not edit.\n */\n'; + var banner = '/**\n * <%= pkg.homepage %>\n * Copyright (c) <%= grunt.template.today("yyyy") %>\n * This file is generated automatically. Do not edit.\n */\n'; + + // Path to WordPress install. Either absoloute or relative to this plugin. + // Change this by passing --abspath="new/path" as a grunt option. + var abspath; + + if ( grunt.option( "abspath" ) ) { + abspath = grunt.option( "abspath" ); + } else if ( 'WP_DEVELOP_DIR' in process.env ) { + abspath = process.env.WP_DEVELOP_DIR; + } else { + abspath = '/tmp/wordpress'; + } + // Project configuration grunt.initConfig( { @@ -135,12 +148,14 @@ module.exports = function( grunt ) { specs: 'js-tests/build/specs.js', helpers: 'js-tests/build/helpers.js', vendor: [ - 'js-tests/vendor/jquery.js', - 'js-tests/vendor/underscore.js', - 'js-tests/vendor/backbone.js', - 'js-tests/vendor/wp-shortcode.js', - 'js-tests/vendor/wp-util.js', - 'js-tests/vendor/wp-editors.js', + abspath + '/wp-includes/js/jquery/jquery.js', + abspath + '/wp-includes/js/underscore.min.js', + abspath + '/wp-includes/js/backbone.min.js', + abspath + '/wp-includes/js/wp-util.js', + abspath + '/wp-includes/js/shortcode.js', + abspath + '/wp-admin/js/editor.js', + abspath + '/wp-includes/js/media-models.js', + abspath + '/wp-includes/js/media-views.js', 'js-tests/vendor/mock-ajax.js', ], } @@ -196,7 +211,7 @@ module.exports = function( grunt ) { grunt.loadNpmTasks( 'grunt-contrib-jasmine' ); grunt.loadNpmTasks( 'grunt-contrib-jshint' ); - grunt.registerTask( 'scripts', [ 'browserify', 'jasmine', 'jshint' ] ); + grunt.registerTask( 'scripts', [ 'browserify', 'jshint' ] ); grunt.registerTask( 'styles', [ 'sass', 'postcss' ] ); grunt.registerTask( 'default', [ 'scripts', 'styles' ] ); grunt.registerTask( 'i18n', ['addtextdomain', 'makepot'] ); diff --git a/css/sass/shortcode-ui.scss b/css/sass/shortcode-ui.scss index 58f2dfdf..5d914394 100644 --- a/css/sass/shortcode-ui.scss +++ b/css/sass/shortcode-ui.scss @@ -16,60 +16,85 @@ } -.add-shortcode-list { - padding: 0 10px; - - .shortcode-list-item { - margin: 10px; - float: left; - -webkit-box-shadow: inset 0 0 15px rgba( 0, 0, 0, 0.1 ), inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); - box-shadow: inset 0 0 15px rgba( 0, 0, 0, 0.1 ), inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); - background: #eee; - cursor: pointer; - position: relative; - text-align: center; - width: 150px; - height: 150px; +.button-shortcode-ui-insert span { + display: inline-block; + width: 18px; + height: 18px; + vertical-align: text-top; + margin: -2px 7px 0 -3px; + left: -2px; + color: #82878c; +} + +.insert-shortcode-list { + + .add-shortcode-list { + padding: 0 10px; - .add-shortcode-list-item-icon { + .shortcode-list-item { + margin: 10px; + float: left; + -webkit-box-shadow: inset 0 0 15px rgba( 0, 0, 0, 0.1 ), inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); + box-shadow: inset 0 0 15px rgba( 0, 0, 0, 0.1 ), inset 0 0 0 1px rgba( 0, 0, 0, 0.1 ); + background: #eee; + cursor: pointer; position: relative; - height: 120px; - font-size: 64px; + text-align: center; + width: 150px; + height: 150px; + + .add-shortcode-list-item-icon { + position: relative; + height: 120px; + font-size: 64px; + + .dashicons, + img { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: inherit; + line-height: inherit; + width: auto; + height: auto; + max-width: 80%; + max-height: 80%; + } - .dashicons, - img { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: inherit; - line-height: inherit; - width: auto; - height: auto; - max-width: 80%; - max-height: 80%; } + .add-shortcode-list-item-title { + box-sizing: border-box; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + margin: 0; + line-height: 1.2; + padding: 8px; + overflow: hidden; + max-height: 100%; + word-wrap: break-word; + text-align: center; + font-weight: bold; + background: rgba( 255, 255, 255, 0.8 ); + -webkit-box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); + box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); + } } + } - .add-shortcode-list-item-title { - box-sizing: border-box; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - margin: 0; - line-height: 1.2; - padding: 8px; - overflow: hidden; - max-height: 100%; - word-wrap: break-word; - text-align: center; - font-weight: bold; - background: rgba( 255, 255, 255, 0.8 ); - -webkit-box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); - box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.15 ); - } + &.has-search { + padding-top: 25px; + } + + .search { + position: absolute; + top: 0px; + right: 10px; + width: 100%; + max-width: 300px; } } diff --git a/css/shortcode-ui-editor-styles.css b/css/shortcode-ui-editor-styles.css index d552a2db..598428bf 100644 --- a/css/shortcode-ui-editor-styles.css +++ b/css/shortcode-ui-editor-styles.css @@ -1,8 +1,10 @@ .wpview-wrap.wp-mce-view-show-toolbar .toolbar { display: block; } + .wpview-wrap .shortcake-error { color: red; font-weight: bold; } + .wpview-wrap .shortcake-empty { font-family: Consolas, Monaco, monospace; color: #666; diff --git a/css/shortcode-ui-editor-styles.css.map b/css/shortcode-ui-editor-styles.css.map index fab02612..ff67e1d8 100644 --- a/css/shortcode-ui-editor-styles.css.map +++ b/css/shortcode-ui-editor-styles.css.map @@ -1 +1 @@ -{"version":3,"sources":["../shortcode-ui-editor-styles.scss"],"names":[],"mappings":"AAGA;EACG,eAAA,EAAA;AAIH;EACE,WAAA;EACA,kBAAA,EAAA;AAGF;EACE,yCAAA;EACA,YAAA;EACA,gBAAA,EAAA;;AAIF;EACC,sBAAA;EACA,YAAA;EACA,iBAAA,EAAA","file":"shortcode-ui-editor-styles.css"} \ No newline at end of file +{"version":3,"sources":["sass/shortcode-ui-editor-styles.scss"],"names":[],"mappings":"AAAA;EAIG,eAAe,EACf;;AALH;EASE,WAAW;EACX,kBAAkB,EAClB;;AAXF;EAcE,yCAAyC;EACzC,YAAY;EACZ,gBAAgB,EAChB;;AAGF;EACC,sBAAsB;EACtB,YAAY;EACZ,iBAAiB,EACjB","file":"shortcode-ui-editor-styles.css"} \ No newline at end of file diff --git a/css/shortcode-ui.css b/css/shortcode-ui.css index c933bd5c..bbdf83cc 100644 --- a/css/shortcode-ui.css +++ b/css/shortcode-ui.css @@ -5,12 +5,22 @@ max-width: 800px; max-height: 800px; margin: auto; } + .shortcode-ui-insert-modal .media-frame-content { top: 54px; } -.add-shortcode-list { +.button-shortcode-ui-insert span { + display: inline-block; + width: 18px; + height: 18px; + vertical-align: text-top; + margin: -2px 7px 0 -3px; + left: -2px; + color: #82878c; } + +.insert-shortcode-list .add-shortcode-list { padding: 0 10px; } - .add-shortcode-list .shortcode-list-item { + .insert-shortcode-list .add-shortcode-list .shortcode-list-item { margin: 10px; float: left; box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.1), inset 0 0 0 1px rgba(0, 0, 0, 0.1); @@ -20,16 +30,16 @@ text-align: center; width: 150px; height: 150px; } - .add-shortcode-list .shortcode-list-item .add-shortcode-list-item-icon { + .insert-shortcode-list .add-shortcode-list .shortcode-list-item .add-shortcode-list-item-icon { position: relative; height: 120px; font-size: 64px; } - .add-shortcode-list .shortcode-list-item .add-shortcode-list-item-icon .dashicons, .add-shortcode-list .shortcode-list-item .add-shortcode-list-item-icon img { + .insert-shortcode-list .add-shortcode-list .shortcode-list-item .add-shortcode-list-item-icon .dashicons, + .insert-shortcode-list .add-shortcode-list .shortcode-list-item .add-shortcode-list-item-icon img { position: absolute; top: 50%; left: 50%; -webkit-transform: translate(-50%, -50%); - -ms-transform: translate(-50%, -50%); transform: translate(-50%, -50%); font-size: inherit; line-height: inherit; @@ -37,7 +47,7 @@ height: auto; max-width: 80%; max-height: 80%; } - .add-shortcode-list .shortcode-list-item .add-shortcode-list-item-title { + .insert-shortcode-list .add-shortcode-list .shortcode-list-item .add-shortcode-list-item-title { box-sizing: border-box; position: absolute; bottom: 0; @@ -54,14 +64,27 @@ background: rgba(255, 255, 255, 0.8); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.15); } +.insert-shortcode-list.has-search { + padding-top: 25px; } + +.insert-shortcode-list .search { + position: absolute; + top: 0px; + right: 10px; + width: 100%; + max-width: 300px; } + .shortcode-ui-content .edit-shortcode-tabs { height: 34px; padding: 10px 10px 0; } + .shortcode-ui-content .edit-shortcode-tabs-content { padding: 10px; border-top: 1px solid #ddd; } + .shortcode-ui-content a.wp-color-result { border-bottom: 1px solid #ccc; } + .shortcode-ui-content .media-toolbar { position: relative; height: auto; } @@ -71,7 +94,8 @@ .edit-shortcode-form label { display: block; clear: both; } - .edit-shortcode-form input, .edit-shortcode-form textarea { + .edit-shortcode-form input, + .edit-shortcode-form textarea { border: 1px solid #ddd; box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.07); background-color: #fff; @@ -116,7 +140,7 @@ .edit-shortcode-form .shortcake-attachment-preview .thumbnail:hover:after { background: rgba(0, 0, 0, 0.1); -webkit-transition: all .3s linear; - transition: all .3s linear; } + transition: all .3s linear; } .edit-shortcode-form .shortcake-attachment-preview:not(.has-attachment) { border: 2px dashed #DDD; border-radius: 2px; @@ -146,7 +170,8 @@ left: 5px; font-size: 22px; text-indent: 0; } - .edit-shortcode-form .shortcake-attachment-preview .loading-indicator, .edit-shortcode-form .shortcake-attachment-preview.loading .button.add { + .edit-shortcode-form .shortcake-attachment-preview .loading-indicator, + .edit-shortcode-form .shortcake-attachment-preview.loading .button.add { display: none; } .edit-shortcode-form .shortcake-attachment-preview.loading .loading-indicator { display: block; @@ -189,14 +214,12 @@ @-webkit-keyframes attachment-preview-loading { 0% { margin-left: -60px; } - 100% { margin-left: 60px; } } @keyframes attachment-preview-loading { 0% { margin-left: -60px; } - 100% { margin-left: 60px; } } /*# sourceMappingURL=shortcode-ui.css.map */ \ No newline at end of file diff --git a/css/shortcode-ui.css.map b/css/shortcode-ui.css.map index 63de7233..0a398fd2 100644 --- a/css/shortcode-ui.css.map +++ b/css/shortcode-ui.css.map @@ -1 +1 @@ -{"version":3,"sources":["../shortcode-ui.scss","../_field-image.scss","shortcode-ui.css"],"names":[],"mappings":"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","file":"shortcode-ui.css"} \ No newline at end of file +{"version":3,"sources":["sass/shortcode-ui.scss","sass/_field-image.scss"],"names":[],"mappings":"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","file":"shortcode-ui.css"} \ No newline at end of file diff --git a/inc/class-shortcode-ui.php b/inc/class-shortcode-ui.php index b761eb2e..18c2f811 100644 --- a/inc/class-shortcode-ui.php +++ b/inc/class-shortcode-ui.php @@ -67,6 +67,7 @@ private function __construct() { private function setup_actions() { add_action( 'admin_enqueue_scripts', array( $this, 'action_admin_enqueue_scripts' ) ); add_action( 'wp_enqueue_editor', array( $this, 'action_wp_enqueue_editor' ) ); + add_action( 'media_buttons', array( $this, 'action_media_buttons' ) ); add_action( 'wp_ajax_bulk_do_shortcode', array( $this, 'handle_ajax_bulk_do_shortcode' ) ); add_filter( 'wp_editor_settings', array( $this, 'filter_wp_editor_settings' ), 10, 2 ); } @@ -273,10 +274,23 @@ public function action_wp_enqueue_editor() { do_action( 'shortcode_ui_loaded_editor' ); } + /** + * Output an "Add Post Element" button with the media buttons. + */ + public function action_media_buttons( $editor_id ) { + printf( '', + esc_attr( $editor_id ), + esc_html__( 'Add Post Element', 'shortcode-ui' ) + ); + } + /** * Output required underscore.js templates in the footer */ public function action_admin_print_footer_scripts() { + echo $this->get_view( 'media-frame' ); // WPCS: xss ok echo $this->get_view( 'list-item' ); // WPCS: xss ok echo $this->get_view( 'edit-form' ); // WPCS: xss ok diff --git a/inc/templates/edit-form.tpl.php b/inc/templates/edit-form.tpl.php index c28c8a2f..09410d2e 100644 --- a/inc/templates/edit-form.tpl.php +++ b/inc/templates/edit-form.tpl.php @@ -1,7 +1,6 @@ diff --git a/js-tests/build/specs.js b/js-tests/build/specs.js index 00c87622..4b2e53af 100644 --- a/js-tests/build/specs.js +++ b/js-tests/build/specs.js @@ -27,7 +27,7 @@ describe( "Shortcode Inner Content Model", function() { } ); -},{"../../js/src/models/inner-content":9}],2:[function(require,module,exports){ +},{"../../js/src/models/inner-content":10}],2:[function(require,module,exports){ var ShortcodeAttribute = require('../../js/src/models/shortcode-attribute'); describe( "Shortcode Attribute Model", function() { @@ -53,7 +53,7 @@ describe( "Shortcode Attribute Model", function() { } ); -},{"../../js/src/models/shortcode-attribute":10}],3:[function(require,module,exports){ +},{"../../js/src/models/shortcode-attribute":11}],3:[function(require,module,exports){ (function (global){ var Shortcode = require('../../js/src/models/shortcode'); var InnerContent = require('../../js/src/models/inner-content'); @@ -153,7 +153,7 @@ describe( "Shortcode Model", function() { }); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../../js/src/collections/shortcode-attributes":7,"../../js/src/models/inner-content":9,"../../js/src/models/shortcode":11,"../../js/src/models/shortcode-attribute":10}],4:[function(require,module,exports){ +},{"../../js/src/collections/shortcode-attributes":7,"../../js/src/models/inner-content":10,"../../js/src/models/shortcode":12,"../../js/src/models/shortcode-attribute":11}],4:[function(require,module,exports){ (function (global){ var Shortcode = require('../../js/src/models/shortcode'); var ShortcodeViewConstructor = require('../../js/src/utils/shortcode-view-constructor'); @@ -305,7 +305,7 @@ describe( 'Shortcode View Constructor', function(){ }); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"../../js/src/models/shortcode":11,"../../js/src/utils/shortcode-view-constructor":13,"../../js/src/utils/sui":14}],5:[function(require,module,exports){ +},{"../../js/src/models/shortcode":12,"../../js/src/utils/shortcode-view-constructor":14,"../../js/src/utils/sui":15}],5:[function(require,module,exports){ (function (global){ var Shortcode = require('./../../../js/src/models/shortcode.js'); var MceViewConstructor = require('./../../../js/src/utils/shortcode-view-constructor.js'); @@ -457,7 +457,7 @@ describe( "MCE View Constructor", function() { it( 'parses shortcode with dashes in name and attribute', function() { var shortcode = MceViewConstructor.parseShortcodeString( '[test-shortcode test-attr="test value 2"]'); expect( shortcode instanceof Shortcode ).toEqual( true ); - expect( shortcode.get( 'attrs' ).findWhere( { attr: 'test-attr' }).get('value') ).not.toEqual( 'test value 2' ); + expect( shortcode.get( 'attrs' ).findWhere( { attr: 'test-attr' }).get('value') ).toEqual( 'test value 2' ); }); // https://github.com/fusioneng/Shortcake/issues/171 @@ -495,7 +495,7 @@ describe( "MCE View Constructor", function() { } ); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./../../../js/src/models/shortcode.js":11,"./../../../js/src/utils/shortcode-view-constructor.js":13,"./../../../js/src/utils/sui.js":14}],6:[function(require,module,exports){ +},{"./../../../js/src/models/shortcode.js":12,"./../../../js/src/utils/shortcode-view-constructor.js":14,"./../../../js/src/utils/sui.js":15}],6:[function(require,module,exports){ var Shortcodes = require('./../../../js/src/collections/shortcodes.js'); var sui = require('./../../../js/src/utils/sui.js'); @@ -507,12 +507,12 @@ describe( "SUI Util", function() { it( 'expected properties', function() { expect( sui.shortcodes instanceof Shortcodes ).toEqual( true ); - expect( sui.views ).toEqual( {} ); + expect( typeof sui.views ).toEqual( 'object' ); }); } ); -},{"./../../../js/src/collections/shortcodes.js":8,"./../../../js/src/utils/sui.js":14}],7:[function(require,module,exports){ +},{"./../../../js/src/collections/shortcodes.js":8,"./../../../js/src/utils/sui.js":15}],7:[function(require,module,exports){ (function (global){ var Backbone = (typeof window !== "undefined" ? window['Backbone'] : typeof global !== "undefined" ? global['Backbone'] : null); var ShortcodeAttribute = require('./../models/shortcode-attribute.js'); @@ -534,7 +534,7 @@ var ShortcodeAttributes = Backbone.Collection.extend({ module.exports = ShortcodeAttributes; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./../models/shortcode-attribute.js":10}],8:[function(require,module,exports){ +},{"./../models/shortcode-attribute.js":11}],8:[function(require,module,exports){ (function (global){ var Backbone = (typeof window !== "undefined" ? window['Backbone'] : typeof global !== "undefined" ? global['Backbone'] : null); var Shortcode = require('./../models/shortcode.js'); @@ -547,7 +547,77 @@ var Shortcodes = Backbone.Collection.extend({ module.exports = Shortcodes; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./../models/shortcode.js":11}],9:[function(require,module,exports){ +},{"./../models/shortcode.js":12}],9:[function(require,module,exports){ +(function (global){ +var Backbone = (typeof window !== "undefined" ? window['Backbone'] : typeof global !== "undefined" ? global['Backbone'] : null), + wp = (typeof window !== "undefined" ? window['wp'] : typeof global !== "undefined" ? global['wp'] : null), + sui = require('./../utils/sui.js'); + +var FrameState = wp.media.controller.State.extend({ + + initialize: function( options ){ + + _.bindAll( this, 'refresh', 'insert', 'reset', 'setShortcode', 'getShortcode' ); + + this.props = new Backbone.Model({ + shortcode: null, + search: null + }); + + if ( 'shortcode' in options ) { + this.setShortcode( options.shortcode ); + } + + // Allow setting a custom insertAction method. + if ( 'insertAction' in options ) { + this.insertAction = options.insertAction; + } + + }, + + insertAction: function( shortcode ) { + send_to_editor( shortcode.formatShortcode() ); + }, + + refresh: function() { + if ( this.frame && this.frame.toolbar ) { + this.frame.toolbar.get().refresh(); + } + }, + + insert: function() { + + var shortcode = this.props.get('shortcode'); + + if ( shortcode ) { + this.insertAction( shortcode ); + this.reset(); + this.frame.close(); + } + }, + + reset: function() { + this.props.set( 'shortcode', null ); + this.props.set( 'search', null ); + }, + + setShortcode: function( shortcode ) { + this.props.set( 'shortcode', shortcode ); + }, + + getShortcode: function( shortcode ) { + return this.props.get( 'shortcode' ); + }, + +}); + +// Make this available globally. +sui.controllers.FrameState = FrameState; + +module.exports = FrameState; + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./../utils/sui.js":15}],10:[function(require,module,exports){ (function (global){ var Backbone = (typeof window !== "undefined" ? window['Backbone'] : typeof global !== "undefined" ? global['Backbone'] : null); @@ -566,7 +636,7 @@ var InnerContent = Backbone.Model.extend({ module.exports = InnerContent; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],10:[function(require,module,exports){ +},{}],11:[function(require,module,exports){ (function (global){ var Backbone = (typeof window !== "undefined" ? window['Backbone'] : typeof global !== "undefined" ? global['Backbone'] : null); @@ -589,7 +659,7 @@ var ShortcodeAttribute = Backbone.Model.extend({ module.exports = ShortcodeAttribute; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],11:[function(require,module,exports){ +},{}],12:[function(require,module,exports){ (function (global){ var Backbone = (typeof window !== "undefined" ? window['Backbone'] : typeof global !== "undefined" ? global['Backbone'] : null); var ShortcodeAttributes = require('./../collections/shortcode-attributes.js'); @@ -627,14 +697,19 @@ Shortcode = Backbone.Model.extend({ * Handles converting the attribute collection to JSON. */ toJSON: function( options ) { + options = Backbone.Model.prototype.toJSON.call(this, options); + if ( options.attrs && ( options.attrs instanceof ShortcodeAttributes ) ) { options.attrs = options.attrs.toJSON(); } + if ( options.inner_content && ( options.inner_content instanceof InnerContent ) ) { options.inner_content = options.inner_content.toJSON(); } + return options; + }, /** @@ -642,12 +717,17 @@ Shortcode = Backbone.Model.extend({ * Make sure we don't clone a reference to attributes. */ clone: function() { + var clone = Backbone.Model.prototype.clone.call( this ); + clone.set( 'attrs', clone.get( 'attrs' ).clone() ); + if ( clone.get( 'inner_content' ) ) { clone.set( 'inner_content', clone.get( 'inner_content' ).clone() ); } + return clone; + }, /** @@ -702,12 +782,14 @@ Shortcode = Backbone.Model.extend({ return template; }, + + }); module.exports = Shortcode; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./../collections/shortcode-attributes.js":7,"./inner-content.js":9}],12:[function(require,module,exports){ +},{"./../collections/shortcode-attributes.js":7,"./inner-content.js":10}],13:[function(require,module,exports){ (function (global){ var $ = (typeof window !== "undefined" ? window['jQuery'] : typeof global !== "undefined" ? global['jQuery'] : null); var _ = (typeof window !== "undefined" ? window['_'] : typeof global !== "undefined" ? global['_'] : null); @@ -822,12 +904,13 @@ var Fetcher = (function() { module.exports = Fetcher; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{}],13:[function(require,module,exports){ +},{}],14:[function(require,module,exports){ (function (global){ -var sui = require('./sui.js'), +var sui = require('./sui.js'), fetcher = require('./fetcher.js'), - wp = (typeof window !== "undefined" ? window['wp'] : typeof global !== "undefined" ? global['wp'] : null), - $ = (typeof window !== "undefined" ? window['jQuery'] : typeof global !== "undefined" ? global['jQuery'] : null); + Frame = require('./../views/media-frame.js'), + wp = (typeof window !== "undefined" ? window['wp'] : typeof global !== "undefined" ? global['wp'] : null), + $ = (typeof window !== "undefined" ? window['jQuery'] : typeof global !== "undefined" ? global['jQuery'] : null); /** * Generic shortcode MCE view constructor. @@ -986,24 +1069,26 @@ var shortcodeViewConstructor = { * @param {string} shortcodeString String representation of the shortcode */ edit: function( shortcodeString ) { - var currentShortcode; + + var shortcode; // Backwards compatability for WP pre-4.2 if ( 'object' === typeof( shortcodeString ) ) { shortcodeString = decodeURIComponent( $(shortcodeString).attr('data-wpview-text') ); } - currentShortcode = this.parseShortcodeString( shortcodeString ); + shortcode = this.parseShortcodeString( shortcodeString ); - if ( currentShortcode ) { + if ( shortcode ) { - var wp_media_frame = wp.media.frames.wp_media_frame = wp.media({ - frame : "post", - state : 'shortcode-ui', - currentShortcode : currentShortcode, + var frame = new Frame({ + shortcodes : sui.shortcodes, + shortcode : shortcode, + title : shortcodeUIData.strings.media_frame_menu_insert_label, + updateTitle : shortcodeUIData.strings.media_frame_menu_update_label, }); - wp_media_frame.open(); + frame.open(); } @@ -1072,139 +1157,1110 @@ var shortcodeViewConstructor = { * @return {string} */ pregQuote: function( str, delimiter ) { - return String(str) - .replace( - new RegExp( '[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + ( delimiter || '' ) + '-]', 'g' ), - '\\$&' ); + var regexp = new RegExp( '[.\\\\+*?\\[\\^\\]$(){}=!<>|:\\' + ( delimiter || '' ) + '-]', 'g' ); + return String( str ).replace( regexp, '\\$&' ); }, - // Backwards compatability for Pre WP 4.2. - View: { +}; - overlay: true, +module.exports = sui.utils.shortcodeViewConstructor = shortcodeViewConstructor; - initialize: function( options ) { - this.shortcode = this.getShortcode( options ); - this.fetch(); - }, +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./../views/media-frame.js":24,"./fetcher.js":13,"./sui.js":15}],15:[function(require,module,exports){ +var Shortcodes = require('./../collections/shortcodes.js'); - getShortcode: function( options ) { +window.Shortcode_UI = window.Shortcode_UI || { + shortcodes: new Shortcodes(), + views: {}, + controllers: {}, + utils: {}, +}; - var shortcodeModel, shortcode; +module.exports = window.Shortcode_UI; - shortcodeModel = sui.shortcodes.findWhere( { shortcode_tag: options.shortcode.tag } ); +},{"./../collections/shortcodes.js":8}],16:[function(require,module,exports){ +var sui = require('./../utils/sui.js'); - if (!shortcodeModel) { - return; +var editAttributeFieldAttachment = sui.views.editAttributeField.extend( { + + events: { + 'click .add' : '_openMediaFrame', + 'click .remove' : '_removeAttachment', + 'click .thumbnail' : '_openMediaFrame', + 'selectAttachment' : '_selectAttachment', + }, + + /** + * Update the field attachment. + * Re-renders UI. + * If ID is empty - does nothing. + * + * @param {int} id Attachment ID + */ + updateValue: function( id ) { + + if ( ! id ) { + return; + } + + this.setValue( id ); + + var self = this; + + if ( editAttributeFieldAttachment.getFromCache( id ) ) { + self._renderPreview( editAttributeFieldAttachment.getFromCache( id ) ); + + // Call the updateValue() function, to trigger any listeners + // hooked on it. + self.triggerCallbacks(); + return; + } + + this.$container.addClass( 'loading' ); + + wp.ajax.post( 'get-attachment', { + 'id': id + } ).done( function( attachment ) { + // Cache for later. + editAttributeFieldAttachment.setInCache( id, attachment ); + self._renderPreview( attachment ); + + // Call the updateValue() function, to trigger any listeners + // hooked on it. + self.triggerCallbacks(); + } ).always( function( attachment ) { + self.$container.removeClass( 'loading' ); + }); + }, + + render: function() { + + // Set model default values. + for ( var arg in ShortcakeImageFieldData.defaultArgs ) { + if ( ! this.model.get( arg ) ) { + this.model.set( arg, ShortcakeImageFieldData.defaultArgs[ arg ] ); } + } - shortcode = shortcodeModel.clone(); + this.$el.html( this.template( this.model.toJSON() ) ); - shortcode.get('attrs').each( - function(attr) { + this.$container = this.$el.find( '.shortcake-attachment-preview' ); + this.$thumbnailDetailsContainer = this.$el.find( '.thumbnail-details-container' ); + var $addButton = this.$container.find( 'button.add' ); - if (attr.get('attr') in options.shortcode.attrs.named) { - attr.set('value', - options.shortcode.attrs.named[attr - .get('attr')]); - } + this.frame = wp.media( { + multiple: false, + title: this.model.get( 'frameTitle' ), + library: { + type: this.model.get( 'libraryType' ), + }, + } ); + + // Add initial Attachment if available. + this.updateValue( this.model.get( 'value' ) ); + + }, + + /** + * Renders attachment preview in field. + * @param {object} attachment model + * @return null + */ + _renderPreview: function( attachment ) { + + var $thumbnail = jQuery('
'); + + if ( 'image' !== attachment.type ) { + + jQuery( '