create and show one use only dialog, constructed based on global state. - ckeditor4.x

I have a plugin which need to show a (Modal) dialog each time the user double click on a word.
Detecting double click is no problem, but the exact fields/values in the dialog depends on exactly which word the user clicked on, and some mutable global state. So I can't create the dialog until the moment before I need to show it. And here is the problem: How do I do that?
Right now I use this code:
var dialogName="uniqueDialog" + counter++;
CKEDITOR.dialog.add(dialogName,function(editor) {
// Creating dialog here.
});
CKEDITOR.instances.editor.openDialog(dialogName);
This works, but having to add a uniquely named dialog, just to show it once and then newer use it again seems really really wrong. Also I fear this will keep using resources since the dialogs are newer removed(I could not find any remove method).
So my question is: Is there a better way to dynamical create and show a "one use" dialog?

Update:
If bootstrap is not allowed then maybe an addFrame version of the dialog is acceptable. This could then refer to a html file that can load from parameters.
NB: The plunkr only works, if you fork and edit it, otherwise it will give you a 404 for the template.
Here is a quick plunkr:
plunky
And here is the plugin in question:
CKEDITOR.plugins.add( 'insertVariable', {
requires: ['iframedialog'],
icons: 'insertvariable',
init: function( editor ) {
editor.addCommand( 'varDialog', new CKEDITOR.dialogCommand( 'varDialog' ) );
CKEDITOR.dialog.addIframe('varDialog','varDialog','sample.html?var='+item,500,400);
editor.ui.addButton( 'insertVariable', {
label: 'Insert Variable',
command: 'varDialog',
icon: this.path + '<insert gif>'
});
}
});
Obviously you are not creating dialogs anymore with different content, but you are referring to another piece of html, that can change. I've kept the bootstrap thing in there as well for reference.
I made one final edit, that will show the current contents. So I think that is roughly what you want. Check it out.
Previous Answer
If you are prepared to use bootstrap, then you can do no worse than check out their modal dialog, which can be just be shown and hidden at will.
http://getbootstrap.com/javascript/#modals
It's simple and cuts down any need to keep creating your own dialog. The dialog won't be one use type, but you will set the defaults as necessary. The varying content link is here:
http://getbootstrap.com/javascript/#modals-related-target
That would be the quickest way to get this going. It all depends on whether you want to use this framework. As CKEDITOR is already using JQuery it is an option worth considering.

Related

Link directly to a notebook page in a view

I have an view that extends the current project view, where we add multiple tabs (notebook pages) to show information from other parts of a project.
One of these pages is an overview page that summarizes what is under the other tabs, and I'd like to link the headlines for each section directly to each displayed page. I've currently solved this by using the index of each tab and calling bootstrap's .tab('show') method on the link within the tab:
$(".overview-link").click(function (e) {
e.preventDefault();
var sel = '.nav-tabs a:eq(' + $(this).data('tab-index') + ')';
$(sel).tab('show');
});
This works since I've attached a data-tab-index="<int>" to each header link in my widget code, but it's brittle - if someone adds a tab later, the current indices will be broken. Earlier I relied on the anchor on each tab, but that broke as well (and would probably break if a new notebook page were inserted as well).
Triggering a web client redirect / form link directly works, but I want to show a specific page in the view:
this.do_action({
type: 'ir.actions.act_window',
res_model: 'my.model.name',
res_id: 'my.object.id',
view_mode: 'form',
view_type: 'form',
views: [[false, 'form']],
target: 'current'
});
Is there any way to link / redirect the web client directly to a specific notebook page tab through the do_action method or similar on FormWidget?
If I understood well you want to select the tab from the JavaScript (jQuery) FormWidget taking into account that the id could change if anybody install another module that adds another tab
Solution 0
You can add a class to the page in the xml form view. You can use the id of the element selected by this class name in order to call the right anchor and select the right tab item. This should happen when the page is completely loaded:
<page class="nb_page_to_select">
$('a[href=#' + $('.nb_page_to_select').attr('id') + ']').click()
NOTE: As you have said the following paragrah I assume that you know where to run this instruction. The solution I suggest is independent of the index.
This works since I've attached a data-tab-index="<int>" to each
header link in my widget code, but it's brittle - if someone adds a
tab later, the current indices will be broken. Earlier I relied on the
anchor on each tab, but that broke as well (and would probably break
if a new notebook page were inserted as well).
Solution 1
When the page is loaded you can get the tab list DOM object like this:
var tablist = $('ul[role="tablist"]')
And then you can click on the specifict tab, selecing by the text inside the anchor. So you don't depend on the tab index:
tablist.find('a:contains("Other Information")').click()
I think if you have two tabs with the same text does not make any sense, so this should be sufficient.
Solution 2
Even if you want to be more specific you can add a class to the notebook to make sure you are in the correct notebook
<notebook class="nt_to_change">
Now you can use one of this expressions in order to select the tab list
var tablist = $('div.nt_to_change ul.nav-tabs[role="tablist"]')
// or
var tablist = $('div.nt_to_change ul[role="tablist"]')
Solution 3
If the contains selector doesn't convince you because it should be equal you can do this as well to compare and filter
tablist.find('a').filter(function() {
return $.trim($(this).text()) === "Other Information";
}).click();
Where "Other Information" is the string of the notebook page
I didn't tried the solution I'm giving to you, but if it doesn't work at least may be it makes you come up with some idea.
There's a parameter for XML elements named autofocus (for buttons and fields is default_focus and takes 1 or 0 as value). If you add autofocus="autofocus" to a page in XML, this page will be the displayed one when you open the view.
So, you can try to add this through JavaScript, when the user clicks on the respective link -which honestly, I don't know how to achieve that by now-. But you can add a distinctive context parameter to each link in XML, for example context="{'page_to_display': 'page x'}". When you click on the link, I hope these context keys will arrive to your JS method.
If not, you can also modify the fields_view_get method (here I wrote how to do that: Odoo - Hide button for specific user) to check if you get the context you've added to your links and add the autofocus parameter to the respective page.
As you said:
This works since I've attached a data-tab-index="" to each header
link in my widget code, but it's brittle - if someone adds a tab
later, the current indices will be broken.
I assume that your app allow multi-user interaction in realtime, so you have to integrate somewhere in your code, an update part function.
This function will trig if something has changed and cleanout the data to rebuilt the index in order to avoid that the current indices will be broken.

ckeditor disable element, prevent user from adding content to it

I've added a plugin that allows the user to add a specially styled div via a dialog. The issue now is, this element should not be clickable inside the edtior. The problem is the users manage it to click inside the div and enter text there and by this screw it up.
I've already spent some time searching the documentation but couldn't find the right approach to do this yet. I'm not asking for code, just some advice how to do it, a pointer to the right API method would be good enough for me. I guess I can somehow access the elements or intercept an users click and prevent them from adding something to my element somehow, I just couldn't yet figure out how to do it.
Use the Widget System.
Widget Tutorial.
Demos.
I've finally managed to get this done by making the elements content not editable. When I create the element in my dialog:
hrElement.setAttribute('contenteditable', false);
When loading the plugin:
init: function (editor) {
editor.on('contentDom', function () {
var stiching = (this.document.getElementsByTag('div'));
console.log(stiching);
for(var i=0;i<stiching.count();i++){
if (stiching.getItem(i).hasClass('stitching')) {
stiching.getItem(i).setAttribute('contenteditable', false);
}
}
});
}
I'm pretty sure this is not the most best solution (don't like to iterate over the elements) but at least it works for me now. Any suggestions how to improve it for future cases are welcome.

The view area of ckEditor sometimes shows empty at the start

I am using the following directive to create a ckEditor view. There are other lines to the directive to save the data but these are not included as saving always works for me.
app.directive('ckEditor', [function () {
return {
require: '?ngModel',
link: function ($scope, elm, attr, ngModel) {
var ck = ck = CKEDITOR.replace(elm[0]);
ngModel.$render = function (value) {
ck.setData(ngModel.$modelValue);
setTimeout(function () {
ck.setData(ngModel.$modelValue);
}, 1000);
}; }
};
}])
The window appears but almost always the first time around it is empty. Then after clicking the [SOURCE] button to show the source and clicking it again the window is populated with data.
I'm very sure that the ck.setData works as I tried a ck.getData and then logged the output to the console. However it seems like ck.setData does not make the data visible at the start.
Is there some way to force the view window contents to appear?
You can call render on the model at any time and it will simply do whatever you've told it to do. In your case, calling ngModel.$render() will grab the $modelValue and pass it to ck.setData(). Angular will automatically call $render whenever it needs to during its digest cycle (i.e. whenever it notices that the model has been updated). However, I have noticed that there are times when Angular doesn't update properly, especially in instances where the $modelValue is set prior to the directive being compiled.
So, you can simply call ngModel.$render() when your modal object is set. The only problem with that is you have to have access to the ngModel object to do that, which you don't have in your controller. My suggestion would be to do the following:
In your controller:
$scope.editRow = function (row, entityType) {
$scope.modal.data = row;
$scope.modal.visible = true;
...
...
// trigger event after $scope.modal is set
$scope.$emit('modalObjectSet', $scope.modal); //passing $scope.modal is optional
}
In your directive:
ngModel.$render = function (value) {
ck.setData(ngModel.$modelValue);
};
scope.$on('modalObjectSet', function(e, modalData){
// force a call to render
ngModel.$render();
});
Its not a particularly clean solution, but it should allow you to call $render whenever you need to. I hope that helps.
UPDATE: (after your update)
I wasn't aware that your controllers were nested. This can get really icky in Angular, but I'll try to provide a few possible solutions (given that I'm not able to see all your code and project layout). Scope events (as noted here) are specific to the nesting of the scope and only emit events to child scopes. Because of that, I would suggest trying one of the three following solutions (listed in order of my personal preference):
1) Reorganize your code to have a cleaner layout (less nesting of controllers) so that your scopes are direct decendants (rather than sibling controllers).
2) I'm going to assume that 1) wasn't possible. Next I would try to use the $scope.$broadcast() function. The specs for that are listed here as well. The difference between $emit and $broadcast is that $emit only sends event to child $scopes, while $broadcast will send events to both parent and child scopes.
3) Forget using $scope events in angular and just use generic javascript events (using a framework such as jQuery or even just roll your own as in the example here)
There's a fairly simple answer to the question. I checked the DOM and found out the data was getting loaded in fact all of the time. However it was not displaying in the Chrome browser. So the problem is more of a display issue with ckEditor. Strange solution seems to be to do a resize of the ckEditor window which then makes the text visible.
This is a strange issue with ckeditor when your ckeditor is hidden by default. Trying to show the editor has a 30% chance of the editor being uneditable and the editor data is cleared. If you are trying to hide/show your editor, use a css trick like position:absolute;left-9999px; to hide the editor and just return it back by css. This way, the ckeditor is not being removed in the DOM but is just positioned elsewhere.
Use this java script code that is very simple and effective.Note editor1 is my textarea id
<script>
$(function () {
CKEDITOR.timestamp= new Date();
CKEDITOR.replace('editor1');
});
</script>
Second way In controller ,when your query is fetch data from database then use th
is code after .success(function().
$http.get(url).success(function(){
CKEDITOR.replace('editor1');
});
I know, that this thread is dead for a year, but I got the same problem and I found another (still ugly) solution to this problem:
instance.setData(html, function(){
instance.setData(html);
});

jqGrid: Create a custom edit form

I am wanting to customise the edit form in jqGrid so that instead of using the table structured layout provided I would like to use my own custom css structured layout for the form elements. How should I go about modifying the edit form to allow me to use my own custom look?
You can definitely achieve this by jquery ui dialog. However I can not put full code for you but helps you in the steps you have to do.
1 design your custom form whatever CSS and style you want to apply.
Suppose this is youe custome form
<div id="dialog-div">
<input type="text">
</div>
2 on jquery dialog open the dialog on your jqgrid editbutton click
$("#edit").click(function(){
var rowdata = $("#coolGrid").jqGrid('getGridParam','selrow');
if(rowdata){
$("#dialog-div").dialog('open');
var data = $("#coolGrid").jqGrid('getRowData',rowdata);
alert(data);
}
});
by default it will close as-
$("#dialog-div").dialog({
autoOpen: false,
});
Now as you get data in variable you can put in your edit form and of jquery dialog button save it according to your logic.
Hope this helps you.
I would recommend you first of all to read (or at least look thorough) the code of form editing module which implement the part which you want to replace. You will see that it consist from more as 2000 lines of code. If you write "I would like to ..." you should understand the amount of work for implementing what you ask. If you are able to understand the code and if you are able to write your modification of the code even using libraries like jQuery UI then you can decide whether it's worth to invest your time to do the work. The main advantage of using existing solutions is saving of the time. What you get in the way is not perfect, but using existing products you could create your own solutions quickly and with acceptable quality. The way to study existing products which you can use for free seems me more effective as large investments in reinventing the wheel.
http://guriddo.net/?kbe_knowledgebase=using-templates-in-form-editing
Using templates in form editing
As of version 4.8 we support templates in the form editing. This allow to customize the edit form in a way the developer want. To use a template it is needed to set the parameter template in the edit add/or add options of navigator. This can be done in navigator or in the editing method editGridRow :
$("#grid").jqGrid("navGrid",
{add:true, edit:true,...},
{template: "template string for edit",...}
{template: "template string for add",...},
...
);
and in editGridRow method:
$("#grid").jqGrid("editGridRow",
rowid,
{template: "template string",...}
);
To place the CustomerID field in the template the following code string should be inserted in the template string
{CustomerID}
With other words this is the name from colModel put in { }
The usual problem with table layouts is when you have columns with different widths, especially with those very wide.
I solved my problem adding the attr colspan to wide columns in the beforeShowForm event.
for example
"beforeShowForm":function() {
$('#tr_fieldnameasinColModel > td.DataTD').attr('colspan',5);
}
It's not fancy but it worked for me. Perhaps there is a more elegant way to do the same.
I could arrange the fields in several columns without having to make the form extrawide.
When user click on edit button the page navigate to another page, based on Id get the details of a row and you can display the values..
Previous answer of Creating a link in JQGrid solves your problem.

Save drop-down history in a Firefox Toolbar

I'm doing some testing on Firefox toolbars for the sake of learning and I can't find out any information on how to store the contents of a "search" drop-down inside the user's profile.
Is there any tutorial on how to sort this out?
Since it's taking quite a bit to get an answer I went and investigate it myself.
Here is what I've got now. Not all is clear to me but it works.
Let's assume you have a <textbox> like this, on your .xul:
<textbox id="search_with_history" />
You now have to add some other attributes to enable history.
<textbox id="search_with_history" type="autocomplete"
autocompletesearch="form-history"
autocompletesearchparam="Search-History-Name"
ontextentered="Search_Change(param);"
enablehistory="true"
/>
This gives you the minimum to enable a history on that textbox.
For some reason, and here is where my ignorance shows, the onTextEntered event function has to have the param to it called "param". I tried "event" and it didn't work.
But that alone will not do work by itself. One has to add some Javascript to help with the job.
// This is the interface to store the history
const HistoryObject = Components.classes["#mozilla.org/satchel/form-history;1"]
.getService(
Components.interfaces.nsIFormHistory2 || Components.interfaces.nsIFormHistory
);
// The above line was broken into 4 for clearness.
// If you encounter problems please use only one line.
// This function is the one called upon the event of pressing <enter>
// on the text box
function Search_Change(event) {
var terms = document.getElementById('search_with_history').value;
HistoryObject.addEntry('Search-History-Name', terms);
}
This is the absolute minimum to get a history going on.
Gustavo,
I wanted to do the same thing - I found an answer here on the Mozilla support forums. (Edit: I wanted to save my search history out of interest, not because I wanted to learn how the Firefox toolbars work, as you said.)
Basically, that data is stored in a sqlite database file called formhistory.sqlite (in your Firefox profile directory). You can use the Firefox extension SQLite Manager to retrieve and export the data: https://addons.mozilla.org/firefox/addon/5817
You can export it as a CSV (comma- separated values) file and open it with Excel or other software.
This has the added benefit of also saving the history of data you've entered into other forms/fields on sites, such as the Search field on Google, etc, if this data is of interest to you.
Gustavo's solution is good, but document.getElemenById('search_with_history').value; is missing a 't' in getElementById

Resources