CKEDITOR jumps to top after editing a link - javascript

If I edit a link in a CKEDITOR-textarea, the cursor always jumps to the top, before the first letter of the content.
This issue only appears in IE, but only on my page. If I go to the CKEDITOR-demopage, it works as required.
I've been searching for similar issues, but didn't find anything. Anybody knows a solution for this?
Edit:
I found the problem: I've got a custom plugin to change my links, this plugin uses element.setValue() to replace the value of the link, this function does the jump to the top. I've also tried setHtml() and setText(), but it's the same problem.
Edit 2: Forgot to add my code:
plugin.js
CKEDITOR.plugins.add('previewLink', {
icons: 'previewLink',
init: function(editor){
editor.addCommand('previewLinkDialog', new CKEDITOR.dialogCommand('previewLinkDialog'));
editor.ui.addButton('previewLink', {
label: 'Preview Link einfügen',
command: 'previewLinkDialog',
toolbar: 'insert'
});
CKEDITOR.dialog.add('previewLinkDialog', this.path + 'dialogs/previewLink.js');
editor.on('doubleclick', function(evt){
var element = evt.data.element;
if(!element.isReadOnly()){
if(element.is('a')){
editor.openDialog('previewLinkDialog');
}
}
});
}
});
dialogs/previewLink.js
CKEDITOR.dialog.add('previewLinkDialog', function(editor){
return {
title: 'Preview Link einfügen',
minWidth: 400,
minHeight: 200,
contents: [
{
id: 'tab-basic',
label: 'Basic Settings',
elements: [
{
type: 'text',
id: 'text',
label: 'Text',
validate: CKEDITOR.dialog.validate.notEmpty("Bitte füllen Sie das Text-Feld aus"),
setup: function(element){
this.setValue(element.getText());
},
commit: function(element){
// The problem happens here
element.setText(this.getValue());
}
},
{
type: 'text',
id: 'link',
label: 'Link',
validate: CKEDITOR.dialog.validate.notEmpty("Bitte füllen Sie das Link-Feld aus"),
setup: function(element){
//this.setValue(element.getAttribute('data-cke-pa-onclick'));
this.setValue(element.getAttribute('data-cke-saved-href'));
},
commit: function(element){
//element.setAttribute('data-cke-pa-onclick', this.getValue());
element.setAttribute('data-cke-saved-href', this.getValue());
}
},
{
type : 'checkbox',
id : 'popup',
label : 'In Popup öffnen',
'default' : 'checked',
onclickString: "openPopUp(this.href, '', iScreen.windowWidth, iScreen.windowHeight, 0, 0, 0, 1, 1, 0, 0, 0, 0); return false;",
setup: function(element){
this.setValue(element.getAttribute('data-cke-pa-onclick') == this.onclickString);
},
commit: function(element){
if(this.getValue() === true){
var onclick = this.onclickString;
element.setAttribute('data-cke-pa-onclick', this.onclickString);
}
else {
element.removeAttribute('data-cke-pa-onclick');
}
}
}
]
}
],
onShow: function() {
var selection = editor.getSelection(),
element = selection.getStartElement();
if (element)
element = element.getAscendant('a', true);
if (!element || element.getName() != 'a' || element.data('cke-realelement')){
element = editor.document.createElement('a');
this.insertMode = true;
}
else
this.insertMode = false;
this.element = element;
if (!this.insertMode)
this.setupContent( this.element );
},
onOk: function() {
var dialog = this,
link = this.element;
this.commitContent(link);
if (this.insertMode)
editor.insertElement(link);
}
};
});

The reason for this problem is that with the setText call you're removing a node that keeps the selection. As a fallback IE will move the selection to the beginning which causes your editor to scroll.
You have a couple of options here.
Select edited link
In your dialog commit function, make sure that the selection is placed on the element that you want to be selected. You can do it with a single line.
commit: function(element){
// The problem happens here
element.setText(this.getValue());
editor.getSelection().selectElement( element );
}
Note that despite your initial caret position, it will always be changed to select the entire link. Still, this is a solution I'd recommend because of its simplicity.
Store a bookmark
If a precise caret position is a crucial factor for you, then you could use bookmarks to store the "snapshot" of the selection before you modify the DOM, to restore it later.
In this particular case you need to use bookmark2 API (standard bookmarks would get removed by DOM manipulation).
commit: function(element){
// The problem happens here.
var sel = editor.getSelection(),
// Create a bookmark of selection before node modification.
bookmark = sel.getRanges().createBookmarks2( false ),
rng;
// Update element inner text.
element.setText(this.getValue());
try {
// Attempt to restore the bookmark.
sel.selectBookmarks( bookmark );
} catch ( e ) {
// It might fail in case when we had sth like: "<a>foo bar ^baz<a>" and the new node text is shorter than the former
// caret offset, it will throw IndexSizeError in this case. If so we want to
// manually put it at the end of the string.
if ( e.name == 'IndexSizeError' ) {
rng = editor.createRange();
rng.setStartAt( element, CKEDITOR.POSITION_BEFORE_END );
rng.setEndAt( element, CKEDITOR.POSITION_BEFORE_END );
sel.selectRanges( [ rng ] );
}
}
}
If you need any more information see the range and selection docs.

To solve the problem, I suggest you may try to set the position of the cursor after calling the function that cause the problem?
// do before your custom action
var ranges = editor.getSelection().getRanges();
// .............your code ................
// after your custom page
ranges[0].setStart(element.getFirst(), 0);
ranges[0].setEnd(element.getFirst(), 0); //cursor
Reference:
http://ckeditor.com/forums/CKEditor/Any-way-to-getset-cursorcaret-location

Related

Programmatic Dijit/Tree Not Appearing in Declarative Dijit/ContentPane

Can anyone help me figure out why this works in Dojo 1.8 but not in 1.9?
In 1.8, the tree is placed within the “pilotTreeContainer” contentpane. In 1.9, the tree is there if you look in Firebug but visually, it just shows a loading graphic. pilotTreeContainer is declared in the template file for the widget that contains this code. All this code is in the postCreate method.
var treeStore = lang.clone( store );
var treeModel = new ObjectStoreModel(
{
store: treeStore,
query: { id: 'all' },
mayHaveChildren: function ( item ) // only ships have the unique attribute
{
return item.unique === undefined;
}
} );
var pilotTree = new Tree(
{
model: treeModel, // do we need to clone?
autoExpand: true,
showRoot: false,
title: 'Click to add',
openOnClick: true,
getDomNodeById: function ( id ) // new function to find DOM node
{
if ( this._itemNodesMap !== undefined && this._itemNodesMap[ id ] !== undefined && this._itemNodesMap[ id ][0] !== undefined ) {
return this._itemNodesMap[ id ][0].domNode;
}
else {
return null;
}
}
} );
this.pilotTree = pilotTree;
this.pilotTreeContainer.set( 'content', pilotTree );
I’ve tried calling startup on both tree and contentpane.
Debugging the dijit/Tree code, it seesm that there is a deferred which never resolves. It is returned from the _expandNode function when called from the _load function (when trying to expand the root node this._expandNode(rn).then).
The part that fails in dijit/Tree is this:
// Load top level children, and if persist==true, all nodes that were previously opened
this._expandNode(rn).then(lang.hitch(this, function(){
// Then, select the nodes specified by params.paths[].
this.rootLoadingIndicator.style.display = "none";
this.expandChildrenDeferred.resolve(true);
}));
Why is the tree not showing? What is going wrong?
Coming back to this (in the hope that it would be solved in Dojo 1.10), I have found a fix.
I abstracted the tree into its own module, adding it to the container with placeAt() instead of using this.pilotTreeContainer.set( 'content', pilotTree );:
// dijit/Tree implementation for pilots
pilotTree = new PilotTree(
{
model: treeModel
} );
// add to container
pilotTree.placeAt( this.pilotTreeContainer.containerNode );
pilotTree.startup();
then forced it to show its content within the tree's startup() method:
startup: function()
{
this.inherited( arguments );
// force show!
this.rootLoadingIndicator.style.display = "none";
this.rootNode.containerNode.style.display = "block";
},

ExtJS 4.2 - Tab Selects Wrong Value in Combobox with typeAhead:true when Typing Fast

When a combobox is used in ExtJS (tested in 4.2, but likely other versions as well), and the "typeAhead: true" option is set, if you the user types in values very quickly and then hits the "tab" on their keyboard, the focus will move to the next field on the screen and the wrong value is set. Because of the tricky nature of this bug, I have created a JSFiddle for it here: http://jsfiddle.net/59AVC/2/
To replicate the bug, very quickly type "975" and then "tab" in the first combobox field. If you hit tab very quickly after you enter the "5" in "975", you will see that the combobox is set to the "970" option instead. I believe this is happening because the "Tab" is causing whatever option is highlighted in the list to be the value that is set, but what is strange is that the "970" is highlighted still after the "5" in "975" is entered, when it should process that event first before the "tab" and it should have changed the selection to be the correct "975".
I have tried adjusting the typeAheadDelay (set to 0 in the example), as well as the queryDelay and everything else I can think of. It looks like ExtJS is simply canceling the lookup that is somehow still running and not finished when the tab is pressed.
Any suggestions on how to work around this bug? Do I need to write my own "typeAhead" auto-complete function to handle this correctly by single threading the events?
Here is the sample JSFiddle code that shows this:
// The data store containing the list of values
var states = Ext.create('Ext.data.Store', {
fields: ['val_number', 'val_name'],
data : [
{"val_number":"970", "val_name":"970 - Name"},
{"val_number":"971", "val_name":"971 - Name"},
{"val_number":"972", "val_name":"972 - Name"},
{"val_number":"973", "val_name":"973 - Name"},
{"val_number":"974", "val_name":"974 - Name"},
{"val_number":"975", "val_name":"975 - Name"}
//...
]
});
Ext.create('Ext.form.ComboBox', {
fieldLabel: 'Choose 1st Value',
store: states,
queryMode: 'local',
displayField: 'val_name',
valueField: 'val_number',
renderTo: Ext.getBody(),
typeAhead: true,
typeAheadDelay: 0,
minChars: 1,
forceSelection: true,
autoSelect: false,
triggerAction: 'all',
queryDelay: 0,
queryCaching: false
});
Ext.create('Ext.form.ComboBox', {
fieldLabel: 'Choose 2nd Value',
store: states,
queryMode: 'local',
displayField: 'val_name',
valueField: 'val_number',
renderTo: Ext.getBody(),
typeAhead: true,
typeAheadDelay: 0,
minChars: 1,
forceSelection: true,
autoSelect: false,
triggerAction: 'all',
queryDelay: 0,
queryCaching: false
});
UPDATED:
Tried this code as suggested, no change in result - still doesn't select correctly:
Ext.define('App.CustomComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.CustomCombobox',
initComponent:function() {
// call parent init component
this.callParent(arguments);
},
onTypeAhead: function() {
console.log('onTypeAhead...');
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
}
});
found the problematic code snippet:
beforeBlur: function() {
this.doQueryTask.cancel();
this.assertValue();
},
the problem is not typeAhead its selectOnTab together with autoSelect which will be set to true from typeahead.
so this happens:
you type "97" a query will fire and the first value (970) will be selected
you type "5" a query will start, but
you press "tab" and the beforeBlur function gets executed.
the query gets canceled and the currently highlighted value becomes the field value
so what can you do?
it is not possible to give the task a callback so assertValue is called after the query finishes :(
you need to disable autoselect again and the only way i found is to override onTypeAhead and to comment out the highlighting:
.
onTypeAhead: function() {
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
}
Thanks to Jandalf, I have some good news. I was able to work out a solution for my needs by extending the combobox and introducing a few fixes. The first was to do as Jandalf suggested (a good starting point) and the next set of fixes was to stop using a DelayedTask if the delay was 0 or less (my config sets "typeAheadDelay" and "queryDelay" to 0). Finally, I had to also do a check in the "assertValue" that is the equivalent of what happens when someone types a regular key to catch the problem where the tab is blurring things before the keyUp is done. Because of this last part, it may not be the perfect solution for everyone, but it was the only thing that could solve my problem. So, here is the code that makes it work for me. I hope someone else will find it useful.
Ext.define('App.CustomComboBox', {
extend: 'Ext.form.field.ComboBox',
alias: 'widget.CustomCombobox',
initComponent:function() {
// call parent init component
this.callParent(arguments);
},
onTypeAhead: function() {
var me = this,
displayField = me.displayField,
record = me.store.findRecord(displayField, me.getRawValue()),
boundList = me.getPicker(),
newValue, len, selStart;
if (record) {
newValue = record.get(displayField);
len = newValue.length;
selStart = me.getRawValue().length;
//Removed to prevent onBlur/Tab causing invalid selections
//boundList.highlightItem(boundList.getNode(record));
if (selStart !== 0 && selStart !== len) {
me.setRawValue(newValue);
me.selectText(selStart, newValue.length);
}
}
},
onPaste: function(){
var me = this;
if (!me.readOnly && !me.disabled && me.editable) {
if (me.queryDelay > 0) {
//Delay it
me.doQueryTask.delay(me.queryDelay);
} else {
//Changed to do immediately instead of in the delayed task
me.doRawQuery();
}
}
},
// store the last key and doQuery if relevant
onKeyUp: function(e, t) {
var me = this,
key = e.getKey();
if (!me.readOnly && !me.disabled && me.editable) {
me.lastKey = key;
// we put this in a task so that we can cancel it if a user is
// in and out before the queryDelay elapses
// perform query w/ any normal key or backspace or delete
if (!e.isSpecialKey() || key == e.BACKSPACE || key == e.DELETE) {
if (me.queryDelay > 0) {
//Delay it
me.doQueryTask.delay(me.queryDelay);
} else {
//Changed to do immediately instead of in the delayed task
me.doRawQuery();
}
}
}
if (me.enableKeyEvents) {
me.callParent(arguments);
}
},
// private
assertValue: function() {
var me = this,
value = me.getRawValue(),
rec, currentValue;
if (me.forceSelection) {
if (me.multiSelect) {
// For multiselect, check that the current displayed value matches the current
// selection, if it does not then revert to the most recent selection.
if (value !== me.getDisplayValue()) {
me.setValue(me.lastSelection);
}
} else {
// For single-select, match the displayed value to a record and select it,
// if it does not match a record then revert to the most recent selection.
rec = me.findRecordByDisplay(value);
if (rec) {
currentValue = me.value;
// Prevent an issue where we have duplicate display values with
// different underlying values.
if (!me.findRecordByValue(currentValue)) {
me.select(rec, true);
}
} else {
//Try and query the value to find it as a "catch" for the blur happening before the last keyed value was entered
me.doRawQuery();
//Get the new value to use
value = me.getRawValue();
//Copy of the above/same assert value check
rec = me.findRecordByDisplay(value);
if (rec) {
currentValue = me.value;
// Prevent an issue where we have duplicate display values with
// different underlying values.
if (!me.findRecordByValue(currentValue)) {
me.select(rec, true);
}
} else {
//This is the original "else" condition
me.setValue(me.lastSelection);
}
}
}
}
me.collapse();
},
doTypeAhead: function() {
var me = this;
if (!me.typeAheadTask) {
me.typeAheadTask = new Ext.util.DelayedTask(me.onTypeAhead, me);
}
if (me.lastKey != Ext.EventObject.BACKSPACE && me.lastKey != Ext.EventObject.DELETE) {
//Changed to not use the delayed task if 0 or less
if (me.typeAheadDelay > 0) {
me.typeAheadTask.delay(me.typeAheadDelay);
} else {
me.onTypeAhead();
}
}
}
});

In CKEditor, how to set link open in a new window as default while removing the target tab?

I want links to open on a new window as default. I tried:
CKEDITOR.on('dialogDefinition', function ( ev ){
if(ev.data.name == 'link'){
ev.data.definition.getContents('target').get('linkTargetType')['default']='_blank';
}
});
It doesn't work. But I figured out that if I remove the following line. It works.
config.removeDialogTabs = 'image:advanced;image:Link;link:advanced;link:target';
But then the problem is now there is the target tab that allow user to change the link target.
What I want to keep the editor as simple as possible and don't want to allow users to change the link target. Yet, I want to set default target as target:_blank. Any suggestions? Thanks!
It seems that if you remove the Target tab, you cannot change the default value to "new window".
However, you can remove all the options in the Target list except "new window", and set it as the default value.
Try the following code:
CKEDITOR.on('dialogDefinition', function(e) {
if (e.data.name === 'link') {
var target = e.data.definition.getContents('target');
var options = target.get('linkTargetType').items;
for (var i = options.length-1; i >= 0; i--) {
var label = options[i][0];
if (!label.match(/new window/i)) {
options.splice(i, 1);
}
}
var targetField = target.get( 'linkTargetType' );
targetField['default'] = '_blank';
}
});
In this case, the Target tab still exists, but there's only one value ("new window") to select, so the users can't change this.
Hope this helps.
Another way to do it to provide the necessary data in onOk function,
see this particular commit
You add target attribute to data object in the onOk function in plugins/link/dialogs/link.js
onOk: function() {
var data = {};
// Collect data from fields.
this.commitContent( data );
// Overwrite, always set target="_blank"
data.target = {
dependent: "",
fullscreen: "",
height: "",
left: "",
location: "",
menubar: "",
name: "_blank",
resizable: "",
scrollbars: "",
status: "",
toolbar: "",
top: "",
type: "_blank",
width: ""
};
//more code below
}
Here is how I updated my links so they open in new tab, without affecting any other links on the page but only those from ckEditor. This is in Angular, but you can use the same idea depending on your platform.
I created a Pipe to transform the link to include a target:
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({
name: 'setlinktarget'
})
export class SetLinkTarget implements PipeTransform {
transform(value: any): any {
return value.split('<a ').join('<a target="_new"');
}
}
I then applied the pipe to my content:
<div [innerHTML]="myRecord.commentsWithHTML | setlinktarget">
This seems much easier than all the other options out there where you have to modify the ckeditor files. Hope this helps someone.
CKEDITOR.on('dialogDefinition', function (ev) {
var dialogName = ev.data.name;
var dialog = ev.data.definition.dialog;
var dialogDefinition = ev.data.definition;
if(dialogName == 'link') {
dialogDefinition.onLoad = function () {
if(dialogName == 'link'){
dialogDefinition.getContents('target').get('linkTargetType')['default']='_blank';
}
dialog.hidePage( 'target' );
};
}
});
//And configure the below
config.removeDialogTabs = 'image:advanced;link:advanced;link:upload;';
Go to ckeditor/plugins/link/dialogs/link.js
and paste the below code:
/* Here we are latching on an event ... in this case, the dialog open event */
CKEDITOR.on('dialogDefinition', function(ev) {
try {
/* this just gets the name of the dialog */
var dialogName = ev.data.name;
/* this just gets the contents of the opened dialog */
var dialogDefinition = ev.data.definition;
/* Make sure that the dialog opened is the link plugin ... otherwise do nothing */
if(dialogName == 'link') {`enter code here`
/* Getting the contents of the Target tab */
var informationTab = dialogDefinition.getContents('target');
/* Getting the contents of the dropdown field "Target" so we can set it */
var targetField = informationTab.get('linkTargetType');
/* Now that we have the field, we just set the default to _blank
A good modification would be to check the value of the
URL field and if the field does not start with "mailto:" or a
relative path, then set the value to "_blank" */
targetField['default'] = '_blank';
}
} catch(exception) {
alert('Error ' + ev.message);
}
});

Custom wp.media with arguments support

How to setup a [add media] button, with:
proper wordpress [media] UI
has size and alignments UI in popup right hand side
can custom popup title and button
size and alignments arguments can send back to be use
Just try to cover most solutions:
use tb_show("", "media-upload.php?type=image&TB_iframe=true"); and window.send_to_editor
problem: has no standard wp.media UI
in js code:
jQuery("#my_button").click(function() {
tb_show("", "media-upload.php?type=image&TB_iframe=true");
return false;
});
window.send_to_editor = function(html) {
console.log(html);
tb_remove();
}
use wp.media({frame: 'post'})
problem: cannot custom UI elements, such as: title, button
in js code:
function clearField(){
#remove file nodes
#...
}
var frame = wp.media({frame: 'post'});
frame.on('close',function() {
var selection = frame.state().get('selection');
if(!selection.length){
clearField();
}
});
frame.on( 'select',function() {
var state = frame.state();
var selection = state.get('selection');
if ( ! selection ) return;
clearField();
selection.each(function(attachment) {
console.log(attachment.attributes);
});
});
frame.open();
use wp.media.editor with wp.media.editor.open( editor_id )
problem: cannot custom UI elements, such as: title, button
in js code: https://wordpress.stackexchange.com/questions/75808/using-wordpress-3-5-media-uploader-in-meta-box#75823
use wp.media with rewrite wp.media.controller.Library and retrieve attachment in select
problem: complicated ..., but once you understand it, it all make sense, and it is my finial solution
in js code:
/**
* Please attach all the code below to a button click event
**/
//create a new Library, base on defaults
//you can put your attributes in
var insertImage = wp.media.controller.Library.extend({
defaults : _.defaults({
id: 'insert-image',
title: 'Insert Image Url',
allowLocalEdits: true,
displaySettings: true,
displayUserSettings: true,
multiple : true,
type : 'image'//audio, video, application/pdf, ... etc
}, wp.media.controller.Library.prototype.defaults )
});
//Setup media frame
var frame = wp.media({
button : { text : 'Select' },
state : 'insert-image',
states : [
new insertImage()
]
});
//on close, if there is no select files, remove all the files already selected in your main frame
frame.on('close',function() {
var selection = frame.state('insert-image').get('selection');
if(!selection.length){
#remove file nodes
#such as: jq("#my_file_group_field").children('div.image_group_row').remove();
#...
}
});
frame.on( 'select',function() {
var state = frame.state('insert-image');
var selection = state.get('selection');
var imageArray = [];
if ( ! selection ) return;
#remove file nodes
#such as: jq("#my_file_group_field").children('div.image_group_row').remove();
#...
//to get right side attachment UI info, such as: size and alignments
//org code from /wp-includes/js/media-editor.js, arround `line 603 -- send: { ... attachment: function( props, attachment ) { ... `
selection.each(function(attachment) {
var display = state.display( attachment ).toJSON();
var obj_attachment = attachment.toJSON()
var caption = obj_attachment.caption, options, html;
// If captions are disabled, clear the caption.
if ( ! wp.media.view.settings.captions )
delete obj_attachment.caption;
display = wp.media.string.props( display, obj_attachment );
options = {
id: obj_attachment.id,
post_content: obj_attachment.description,
post_excerpt: caption
};
if ( display.linkUrl )
options.url = display.linkUrl;
if ( 'image' === obj_attachment.type ) {
html = wp.media.string.image( display );
_.each({
align: 'align',
size: 'image-size',
alt: 'image_alt'
}, function( option, prop ) {
if ( display[ prop ] )
options[ option ] = display[ prop ];
});
} else if ( 'video' === obj_attachment.type ) {
html = wp.media.string.video( display, obj_attachment );
} else if ( 'audio' === obj_attachment.type ) {
html = wp.media.string.audio( display, obj_attachment );
} else {
html = wp.media.string.link( display );
options.post_title = display.title;
}
//attach info to attachment.attributes object
attachment.attributes['nonce'] = wp.media.view.settings.nonce.sendToEditor;
attachment.attributes['attachment'] = options;
attachment.attributes['html'] = html;
attachment.attributes['post_id'] = wp.media.view.settings.post.id;
//do what ever you like to use it
console.log(attachment.attributes);
console.log(attachment.attributes['attachment']);
console.log(attachment.attributes['html']);
});
});
//reset selection in popup, when open the popup
frame.on('open',function() {
var selection = frame.state('insert-image').get('selection');
//remove all the selection first
selection.each(function(image) {
var attachment = wp.media.attachment( image.attributes.id );
attachment.fetch();
selection.remove( attachment ? [ attachment ] : [] );
});
//add back current selection, in here let us assume you attach all the [id] to <div id="my_file_group_field">...<input type="hidden" id="file_1" .../>...<input type="hidden" id="file_2" .../>
jq("#my_file_group_field").find('input[type="hidden"]').each(function(){
var input_id = jq(this);
if( input_id.val() ){
attachment = wp.media.attachment( input_id.val() );
attachment.fetch();
selection.add( attachment ? [ attachment ] : [] );
}
});
});
//now open the popup
frame.open();
I would like to add to ZAC's option 4 because when I used his code, the image src="" was missing.
Here is the fix
if ( 'image' === obj_attachment.type ) {
html = wp.media.string.image( display );
_.each({
align: 'align',
size: 'image-size',
alt: 'image_alt'
}, function( option, prop ) {
if ( display[ prop ] )
options[ option ] = display[ prop ];
});
html = wp.media.string.image( display, obj_attachment );
}
This way you can call the new media uploader with custom title and button and right side bar.
var custom_uploader;
jQuery('#fileform').on('click','.select-files', function(e) {
var button = jQuery(this);
custom_uploader = wp.media.frames.file_frame = wp.media({
title: 'Choose File',
library: {
author: userSettings.uid // specific user-posted attachment
},
button: {
text: 'Choose File'
},
multiple: false
});
//When a file is selected, grab the URL and set it as the text field's value
custom_uploader.on('select', function() {
attachment = custom_uploader.state().get('selection').first().toJSON();
console.log(attachment.url);
console.log(attachment.id); // use them the way you want
});
//Open the uploader dialog
// Set post id
wp.media.model.settings.post.id = jQuery('#post_ID').val();
custom_uploader.open();
});
Check this link -> https://github.com/phpcodingmaster/WordPress-Media-Modal-Image-Uploads
It will show you how to:
Open the admin media modal
Get single image info
Get multiple images info
Tested with WordPress Version 6.0

making certain cells of an ExtJS GridPanel un-editable

I currently have a GridPanel with the Ext.ux.RowEditor plugin. Four fields exist in the row editor: port, ip address, subnet and DHCP. If the DHCP field (checkbox) of the selected row is checked, I need to make the other three fields un-editable.
I've been trying to perform this code when the beforeedit event is triggered, but to no avail... I've only found ways to make the entire column un-editable. My code so far:
this.rowEditor.on({
scope: this,
beforeedit: this.checkIfEditable
});
checkIfEditable:function(rowEditor, rowIndex) {
if(this.getStore().getAt(rowIndex).get('dhcp')) {
// this function makes the entire column un-editable:
this.getColumnModel().setEditable(2, false);
// I want to make only the other three fields of the current row
// uneditable.
}
}
Please let me know if any clarification is needed.
Any help potentially extending RowEditor to accomplish the target functionality would be greatly appreciated as well!
You can add into RowEditor.js within the function startEditing the call to the function isCellEditable
//.... RowEditor.js
startEditing: function(rowIndex, doFocus){
//.... search for the starting of the below loop into the source code
var cm = g.getColumnModel(), fields = this.items.items, f, val;
for(var i = 0, len = cm.getColumnCount(); i < len; i++){
val = this.preEditValue(record, cm.getDataIndex(i));
f = fields[i];
f.setValue(val);
// *** here add a call to isCellEditable *** //
f.setDisabled(!cm.isCellEditable(i, rowIndex));
// ***************************************** //
this.values[f.id] = Ext.isEmpty(val) ? '' : val;
}
//....
Then override the isCellEditable of your column model and disabled the field based on you condition:
YYY.getColumnModel().isCellEditable = function(colIndex, rowIndex){
if (grid.getStore().getAt(rowIndex).get('dhcp')) {
// you can do more check base on colIndex
// only to disabled the one you want
return false;
}
return Ext.grid.ColumnModel.prototype.isCellEditable.call(this, colIndex, rowIndex);
}
As the docs state:
If the listener returns false
the editor will not be activated.
So...
this.rowEditor.on({
scope: this,
beforeedit: this.checkIfEditable
});
checkIfEditable:function(rowEditor, rowIndex) {
if(this.getStore().getAt(rowIndex).get('dhcp')) {
return false;
}
}
Simply returning false will be enough to cancel the editing ability.
Gotcha.
Interesting idea - a bit of a hassle to implement, but possible.
You need to approach this from two directions:
1 ) edit starts
2 ) checkbox is checked/unchecked
For the first part, I think you could use almost the same code I have above, remove the 'return false' and use the reference to the rowEditor to loop through the items collection, disabling (call the disable method on them) the fields that are not your checkbox field.
The second part of this is to add a handler to the checkbox which would do the same thing.
The following works to make a specific cell uneditable (add the event to the roweditor):
beforeedit: function (roweditor, rowIndex) {
var data = roweditor.grid.store.getAt(rowIndex).data;
var cm = roweditor.grid.getColumnModel();
// disable the first column (can not be edited)
if (** make your check here ***) {
var c = cm.getColumnAt(0);
c.editor.disable();
}
roweditor.initFields();
}
As of ExtJS 3.4 you can just override isCellEditable, as in the example here:
http://docs.sencha.com/ext-js/3-4/#!/api/Ext.grid.ColumnModel-method-isCellEditable
Here's the simpler version :
http://jsfiddle.net/snehalmasne/8twwywcp/
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
,pluginId: 'editing'
})
]
Provide the condition below for the cells to make the un-editable :
grid.on('beforeedit', function(editor, e) {
if (e.record.get('phone') == '555-111-1224')
return false;
});
just set the column in your columnModel/columns to editable:false for fields that should not be editable.
columns: [
{ header: "Ticker", width: 60, editable:false },
{ header: "Company Name", width: 150, id: 'company'},
{ header: "Market Cap."},
{ header: "$ Sales", renderer: money},
{ header: "Employees", resizable: false}
]
I ran into the same problem. Rather than using the GridPanel with the Ext.ux.RowEditor plugin, I used the Ext.grid.EditorGridPanel. In this case, you can specify the editor for each of the other three fields (port, ip address, and subnet) in the column model with a beforeshow event handler as follows:
editor: new Ext.form.TextArea({
height:80,
allowBlank: false,
listeners:{
beforeshow: function(column) {
if (column.gridEditor.record.get('dhcp')) {
column.gridEditor.cancelEdit();
}
}
}
})
Ha!
I made a dirty one, I added an event this.fireEvent('starting', this, fields,record); AT THE END of startEditing
startEditing: function(rowIndex, doFocus){
if(this.editing && this.isDirty()){
this.showTooltip(this.commitChangesText);
return;
}
if(Ext.isObject(rowIndex)){
rowIndex = this.grid.getStore().indexOf(rowIndex);
}
if(this.fireEvent('beforeedit', this, rowIndex) !== false){
this.editing = true;
var g = this.grid, view = g.getView(),
row = view.getRow(rowIndex),
record = g.store.getAt(rowIndex);
this.record = record;
this.rowIndex = rowIndex;
this.values = {};
if(!this.rendered){
this.render(view.getEditorParent());
}
var w = Ext.fly(row).getWidth();
this.setSize(w);
if(!this.initialized){
this.initFields();
}
var cm = g.getColumnModel(), fields = this.items.items, f, val;
for(var i = 0, len = cm.getColumnCount(); i < len; i++){
val = this.preEditValue(record, cm.getDataIndex(i));
f = fields[i];
f.setValue(val);
this.values[f.id] = Ext.isEmpty(val) ? '' : val;
}
this.verifyLayout(true);
if(!this.isVisible()){
this.setPagePosition(Ext.fly(row).getXY());
} else{
this.el.setXY(Ext.fly(row).getXY(), {duration:0.15});
}
if(!this.isVisible()){
this.show().doLayout();
}
if(doFocus !== false){
this.doFocus.defer(this.focusDelay, this);
}
/*I ADDED THIS EVENT ---- contains the fields and record
*/
this.fireEvent('starting', this, fields,record);
}
}
THEN
var editor = new Ext.ux.grid.RowEditor({
saveText: 'Update',
listeners : {
'starting' : function(rowEditor, fields, record){
if(!record.data.equipo_id){
fields[4].setDisabled(false);
}else{
fields[4].setDisabled(true);
}
},
Using Ext JS 4 and the RowEditing plugin i managed to achieve this using something like
rowEditor.on('beforeedit', function (context) {
this.editor.query('textfield')[0].setDisabled(/* your condition here */);
});
the record data is available through context.record.data

Categories

Resources