We used vis.js to create a timeline , We need to localize the name of groups and the content of items with different languages . we tried to use the $translate = $filter('translate') to translate the name of group in angularjs but it works only when we reloading the screen . We also tried for the translation of items to use the template handlebars engine to translate the items but The problem is that Angular seems not to proceed the templates used by Handlebars when we put "{{ 'timeline.content' | translate }} it gives an error . so is there any idea or a way to localize name of groups and items in vis.js .
You can use the configuration functions groupTemplate or template
var config = {
...
groupTemplate: function (group) {
var groupTemplate = document.createElement('div');
// assuming that you have stored the label key in the content, instead of text.
groupTemplate.innerText = translate.get(group.content);
// or whatever function call you need to make to get the translated label;
return groupTemplate;
}
...
}
You can do the same with the actual items in the timeline.
var config = {
...
template: function (item) {
var itemTemplate = document.createElement('div');
// assuming that you have stored the labels in the content, instead of text.
itemTemplate.innerText = translate.get(item.content)
// or whatever function call you need to make to get the translated label;
return itemTemplate;
}
...
}
You can also attach event listeners, or do whatever with templates as they render to a usual DOM elements.
Hope this helps.
Related
I have implemented a jqgrid in grouping method. By default I have kept the groups collapsed using groupCollapse:true parameter of jqgrid. My grid works well but When I expand the group and sort a column, the whole grid is reloaded and the expanded state of the column is not retained. How can I retain the expanded state while sorting?
Please write always which version of jqGrid, which you use (can use), and from which fork (free jqGrid, commercial Guriddo jqGrid JS or an old jqGrid in version <=4.7).
Your requirements could be easy realized in "free jqGrid", which I develop. It allows to use groupCollapse as callback function, which returns Boolean (see the issue). In combination with onClickGroup callback or jqGridGroupingClickGroup event one can easy persist the grouping state.
UPDATED: I created the demo https://jsfiddle.net/92da8xhq/, which demonstrates how one can persist the collapsing state in the grouping grid. Below I describe shortly the code. The demo uses one level of grouping to make the code more simple for understanding.
I added custom collapsedGroups: {} parameter to jqGrid. We will use the parameter to hold the list of collapsed groups. I used collapsedGroups: { "test2": true } in the demo to demonstrated that we can create the grid with some collapsed groups at the beginning. We don't use the value of the property of collapsedGroups object. Just the existence of the property test2 for example means that the group with the value test2 has collapsed state.
The demo uses groupCollapse property of groupingView defined as the callback function. The function tests whether the group is in the list of collapsed groups (has collapsedGroups property with some value)
groupingView: {
groupField: ["name"],
groupCollapse: function (options) {
var collapsedGroups = $(this).jqGrid("getGridParam", "collapsedGroups") || {};
// options looks like { group: number, rowid: string }
if (collapsedGroups.hasOwnProperty(options.group.value)) {
return true;
}
return false;
}
}
We adjust additionally the properties of the custom collapsedGroups parameter after expanding/collapsing of the group. We use the following onClickGroup callback:
onClickGroup: function (hid, isCollapsed) {
var p = $(this).jqGrid("getGridParam"),
iGroup = $(this).jqGrid("getGroupHeaderIndex", hid),
group = p.groupingView.groups[iGroup];
if (p.collapsedGroups == null) {
// be sure that the custom parameter is initialized as an empty object
p.collapsedGroups = {};
}
if (isCollapsed) {
// we place group.value in the p.collapsedGroups object as a property
if (!p.collapsedGroups.hasOwnProperty(group.value)) {
// create the property group.value in with some value
p.collapsedGroups[group.value] = true;
}
} else if (p.collapsedGroups.hasOwnProperty(group.value)) {
// remove group.value property from the p.collapsedGroups object
delete p.collapsedGroups[group.value];
}
}
groupingView: {
groupCollapse: true,
groupField: ["name"],
plusicon: 'ace-icon fa fa-plus-square purple',
minusicon: 'ace-icon fa fa-edit red'
}
I am using jspdf to convert an HTML page to PDF using fromHTML(). The HTML page includes multiple images, which I need fromHTML() to ignore in order to generate the PDF.
I want to use the elementHandler to ignore the images. However, I can only get that to work with a single element ID. Here is the way the documentation shows:
var elementHandler = {
'#ignorePDF': function (element, renderer) {
return true;
}
};
I have tried to replace the '#ignorePDF' ID reference to a reference with a class that applies to all of the images:
'.ignorePDF'
or to include multiple ID's (one for each image):
'#ignorPDF1,#ignorePDF2'
but neither of those approaches has worked for me. Is there another way to accomplish this?
i figured out both issues. To reference multiple items to ignore, set it up like this:
var elementHandler = {};
elementHandlers["#img1"] = function...
elementHandlers["#img2"] = function...
also best to create a function that you can reuse rather than defining it over and over.
As for the inability to use a variable for the key, that was a dumb javascript error on my part. The variable name can be used like this:
var img1 = "#img1";
elementHandlers[img1] = function...
The # character must be included.
It would be useful if the method were modified to permit a class value to be entered so that a single class could be used to denote all items to be ignored.
You can ignore all objects of the same class (called for example no-print), as follows:
var noprints = document.getElementsByClassName("no-print");
var elementHandler = {};
for (var i=0; i<noprints.length; i++) {
elementHandler['#'+noprints[i].getAttribute('id')] = function (element, renderer) { return true; }
};
I want to add unknown number of images based on how many is there for specific user; i have used .Append function for div,labels,inputs and its work fine but when i want to append with source its not working !
so how can i do this ?
i have tried this:
` string sc = "$('<img/>', {src=\"~/Content/themes/img2.jpg\" alt=\"\",class:'table-wrapper'}).appendTo(finalDalata);";
return JavaScript(sc)`
finalDalata is my Div name
so whats iam doing wrong ?
Your appendTo code is invalid, it needs a selector, and your image attributes are not formatted properly, so it should be:
string sc = "$('<img/>', { src:'~/Content/themes/img2.jpg', alt:'', class:'table-wrapper'}).appendTo('#finalDalata');"
However, controller should return data and not control (exploits and injections comes to mind), so a better way would be to create an AppendImage function on the View side in javascript:
function AppendImage(attributes)
{
$('<img/>', attributes).appendTo('#finalDalata');
}
Note: you should probably sanitize the attributes before to be sure.
Then return a list of attributes only:
var images = new List<dynamic>();
// Do this for each images
images.Add(new { src="~/Content/themes/img2.jpg", alt="", class="table-wrapper"});
return Json(images);
And in the ajax success you can:
success: function (data) {
data.foreach( function (item) {
AppendImage(item);
});
}
Solved by TagBuilder
TagBuilder tag = new TagBuilder("img");
tag.Attributes.Add("id", "myImage");
tag.Attributes.Add("src", "/imgs/" + UserName + ".jpg");
tag.Attributes.Add("alt", "my image");
tag.Attributes.Add("class", "imgsClass");
then convert tag to string and pass it back to view
thnx
I'm wondering if it's possible to add a stylesheet or styling rules to the iframe on a RichTextArea field?
I need to make a couple of CSS tweaks to the default styling but I can't target the RichTextArea through my application stylesheet because it's loaded within an iframe.
The "problem" with the Vaadin RichTextArea component is not only in the fact that the editor field is inside an iframe element, but as with all the other Vaadin components, you also have to keep in mind that your components will not be available when the DOM ready callback (i.e. for example $(document).ready(function() {}) if using jQuery or the callback bound to a DOMContentLoaded event) will execute.
This is because, as you know, when the Vaadin application starts, you actually don't have your components inside the DOM yet, but a vaadin bootstrap process will request and take care of the rendering of your UI for you. This is actually the principle with whom GWT works also (see How does GWT provide the correct Javascript code to every browser e.g. to carry out i18n and browser compatibility?) (after all Vaadin is based on GWT).
So e.g. if you use jQuery and you have a script like this loaded at the very beginning right after the vaadinBootstrap.js script loads and executes:
$(function() {
// this code will execute, but no components are available yet.
var rTa = $(".v-richtextarea"); // this won't select your Rich text area
var len = rTa.length // len will be 0 here, as no element matches the previous selector because as stated before, there is not an element with such a class in the DOM yet.
});
After this code executes, the very "heavy" process of creating the UI components and your layout begins, your widgetsets and/or the default one get loaded, and after that you have your beautiful UI set up and ready to interact with the user.
In order to customise an existent component such a RichTextArea and e.g. add a style element to the body of its iframe element, you can certainly venture into the depths of GWT and use JSNI as you did in your answer, but there's also another way to do it, in my opinion, more compact, simple, and does not require the usage of JSNI.
All you need to do is to implement a JavaScriptExtension with a connector on the client side for your component (you can just extend Vaadin's RichTextArea), check out this simple code example:
#!java
package com.package.example;
#JavaScript({"vaadin://js/src/rich_text_area_connector.js"})
public class RichTextAreaExtension extends AbstractJavaScriptExtension {
#Override
public void extend(AbstractClientConnector connector) {
super.extend(connector);
}
}
This is the extension, then you would need to create the client side connector, which is basically a JavaScript file with a function which name is based on the package name of the extension and, of course, the extension's class name:
#!javascript
com_package_example_RichTextAreaExtension = function() {
var connectorParentId = this.getParentId();
var element = this.getElement(parentId); // this is the rich text area element, which at this point is
// If you are using jQuery, then you can just select your element like so:
var jQueryElement = $(element);
// and do whatever you would normally do with the element like
// when you are inside $(document).ready(function() {});
// or you can add a style element to the head element inside the iframe, doing something like the following:
$(element).find("iframe").contents().find('head')
.append('<link rel="stylesheet" href="./VAADIN/themes/your_theme/style_for_richtextarea_body.css" type="text/css" />');
}
And you are done. Another benefit, as you can see is that you don't have to write different code for different browsers (Mozilla, Chrome, IE), you can just use jQuery and the library will handle the compatibility for you. The last part is the extended component itself. As I said before, you can just extend Vaadin's RichTextArea:
public class RichTextAreaWithStyleOnBody extends RichTextArea {
public RichTextAreaWithStyleOnBody(String caption, Property<?> dataSource) {
super(caption);
if (dataSource != null)
setPropertyDataSource(dataSource);
new RichTextAreaExtension().extend(this);
}
public RichTextAreaWithStyleOnBody(String caption, Property<?> dataSource) {
this(caption, dataSource);
}
public RichTextAreaWithStyleOnBody(String caption) {
this(caption, null);
}
public RichTextAreaWithStyleOnBody(Property<?> dataSource) {
this(null, dataSource);
}
public RichTextAreaWithStyleOnBody() {
this(null, null);
}
}
Note the usage of the JavaScript extension inside the main constructor. And finally you can use it in your layout just as you would with any other component:
// Inside your UI's class
#Override
protected void init(VaadinRequest request) {
VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
RichTextArea rTa = new RichTextAreaWithStyleOnBody("A rich text area with a styled body");
rTa.setStyleName("myRichTextArea"); // you can do whatever you'll like on the server side just because your rich text area extends a Vaadin server side component.
rTa.setSizeFull();
layout.addComponent(rTa);
}
As far as I know it is not possible. I had the same problem and wrote a little add-on based on a copy of the vaadin code from the RichTextArea. I added some additional methods to set font-family and font-size. Unfortunately I didn't have enough time recently to clean up my code publish a new version of the add-on.
The main functionality of the add-on is to decouple the toolbar from the area.
You can find the code in the v7 branch here: https://gitorious.org/richtexttoolbar-vaadin-addon/richtexttoolbar-vaadin-addon
After continued searching I found this discussion, which led me to a solution:
https://groups.google.com/forum/#!msg/Google-Web-Toolkit/9kwAJNhnamY/1MVfFFRq8tUJ
I ended up wrapping the RichTextImpl* classes, cloning the initElement() method from the parent class, and inserting these lines...
...for Mozilla/Safari:
_this.#com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.designMode = 'On';
var doc = _this.#com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document;
head=doc.getElementsByTagName('head')[0];
link=document.createElement('link');
link.setAttribute('rel',"stylesheet");
link.setAttribute('href',"/path/to/richtext.css" );
link.setAttribute('type',"text/css");
head.appendChild(link);
...for IE:
var ct = "<html><head><style>#import url('/path/to/richtext.css');</style></head><body CONTENTEDITABLE='true'></body></html>" ;
doc.write( ct );
... to get a style sheet loading in my RichTextArea fields.
Hy guys, i don't know if ist still important but i did something different.
I had a token function made for my richtextarea, and for the token that a was saving in the db just save it with a style class that i already have in my "styles.css" declared.
Step by step is something like this, i am taking the stylesheet and looking for my token classes, and then i am adding the styles in my iframes header, so that my token class in the iframe is styled.
function styleRichtextareaIframe(id, themeClass, tokenClass, tokenSelectedClass) {
setTimeout(function() {
// iframe by id selected
var $iframe = $("#" + id).find(".gwt-RichTextArea");
var $head = $iframe.contents().find("head");
var $body = $iframe.contents().find("body");
var classNameToken = "." + themeClass + " " + "." + tokenClass;
var classNameToken_selected = "." + themeClass + " " + "." + tokenSelectedClass;
var fontClass = "." + themeClass + ".v-app, " + "." + themeClass + " " + ".v-window";
var styleSheets = window.document.styleSheets;
var styleSheetsLength = styleSheets.length;
var style = document.createElement('style');
style.type = 'text/css';
for (var i = 0; i < styleSheetsLength; i++) {
var classes = styleSheets[i].rules || styleSheets[i].cssRules;
for (var x = 0; x < classes.length; x++) {
if (classes[x].selectorText == classNameToken) {
style.appendChild(document.createTextNode(getClassFor(classes[x])));
}
if (classes[x].selectorText == classNameToken_selected) {
style.appendChild(document.createTextNode(getClassFor(classes[x])));
}
if (classes[x].selectorText == fontClass) {
style.appendChild(document.createTextNode(getClassFor(classes[x])));
}
}
}
function getClassFor(classObj) {
if (classObj.cssText) {
return classObj.cssText;
} else {
return classObj.style.cssText;
}
}
//Adding the classes also to the body
//of dourse you can change the output string to just give you the style class that you need.
$body.addClass("v-app " + themeClass);
$head.append(style);
}, 200);
}
I had to inset the timeout function because of the "problem" with the Vaadin RichTextArea component is not only in the fact that the editor field is inside an iframe element, but as with all the other Vaadin components, you also have to keep in mind that your components will not be available when the DOM is ready, (THE COMPONENT IS NOT IN THE DOM).
Hope this help someone, and sorry for my bad english.
Cheers.
Simplest way I came up with was extending the RichTextArea component and setting a custom style with JavaScript:
public class MyRichTextArea extends RichTextArea {
public MyRichTextArea(String className) {
setStyleName(className);
String js = "var iframeContainer = document.getElementsByClassName('" + className + "')[0];" +
"var iframe = iframeContainer.getElementsByTagName('iframe')[0];" +
"var iframeBody = iframe.contentDocument.getElementsByTagName('body')[0];" +
"iframeBody.style.fontFamily='Arial';";
JavaScript.getCurrent().execute(js);
}
}
Usage:
MyRichTextArea field = new MyRichTextArea("fieldClassName");
Please note that iframe.contentDocument should be supported with all major browsers, but for better support additional tweaks should be added - see Getting the document object of an iframe
CakePHP URL query parameters are not done in a standard fashion e.g. the params are /param1:value1/param2:value2 instead of ?param1=value1¶m2=value2
This means that the javascript location.search does not return a value.
There is a getQueryParams JQuery plugin that does what I want using location.search
I have had to modify this to use
var pairs = location.pathname.split('/');
instead of
var pairs = location.search.substring(1).split('&');
However this now includes everything except the host in the variable pairs. So I have to check for a ':' to see if it is a parameter.
This works - but is there a better (more Cake like) way of doing it? I don't want to improve on the JQuery plugin (e.g. Regex), I want to find a better way to integrate the plugin with CakePHP.
Upddate: I've removed the rest of the JQuery code as I'm happy with the jquery code, my issue is with fitting it more with cake
Is there some 'Cake like' way of removing the path to your app, the model and the controller from location.pathname so that you end up what you would normally get from location.search?
Since you're searching for a particular parameter, you can use a regular expression:
$.getQueryParam = function (param) {
var re = new RegExp(param+':([^\/]+)');
var matches = location.pathname.match(re);
if (matches.length) {
return matches[1];
}
return undefined;
}
So it appears there isn't a better way of doing it. Here is the javascript for reference:
// jQuery getQueryParam Plugin 1.0.1 (20100429)
// By John Terenzio | http://plugins.jquery.com/project/getqueryparam | MIT License
// Modified by ICC to work with cakephp
(function ($) {
// jQuery method, this will work like PHP's $_GET[]
$.getQueryParam = function (param) {
// get the pairs of params fist
// we can't use the javascript 'location.search' because the cakephp URL doesn't use standard URL params
// e.g. the params are /param1:value1/param2:value2 instead of ?param1=value1¶m2=value2
var pairs = location.pathname.split('/');
// now iterate each pair
for (var i = 0; i < pairs.length; i++) {
// cakephp query params all contain ':'
if (pairs[i].indexOf(':') > 0) {
var params = pairs[i].split(':');
if (params[0] == param) {
// if the param doesn't have a value, like ?photos&videos, then return an empty srting
return params[1] || '';
}
}
}
//otherwise return undefined to signify that the param does not exist
return undefined;
};
})(jQuery);