I've got a web page with a US map that has clickable paths that generate a popup, coded with Raphael.js and a paths file containing all the state's paths. I loop through the paths file to create the paths and set the onClick and hover functions for the popups.
I have one path that is not a state, it is a border around the misplaced Alaska and Hawaii. Problem is when I loop through the code, it gets set to fill and onClick functions same as the other paths.
I know I need a conditional in there to change attributes for that one path, but I'm not savvy enough to be able to code it correctly. Maybe access the DOM through node, but I'm not sure how/where.
Map created with this script.
paths set with /js/paths.js
css is /css/default.css
I've searched the archive and gotten close to a solution from info posted there, but I'm banging my head trying to tweak it to work - any help GREATLY appreciated!!!
The border is called "BD" so you can do something like this or even create a different borderAttributes object and assign different attributes when country == "BD"
for (var country in paths) {
var obj = r.path(paths[country].path);
if (country != "BD") {
obj.attr(attributes);
}
arr[obj.id] = country;
Related
I'm using Code Mirror to create a simple code editor that creates shapes on a canvas based on certain commands. I would like to use Code Mirror's annotations to store a shape ID in the line's metadata. That way when I delete a line on the commands I can easily tell the Canvas which shape to delete.
I've been looking through the code mirror's documentation for a while now and still have no idea how I would go about implementing this.
How can I access the metadata (annotations) in text with CodeMirror?
Any help is much appreciated.
Okay, I figured it out. CodeMirror has a function "markText" which can be used to give a className to the line for specific CSS styling. It can also be used to pass in any other parameters including user-created ones like shapeID.
// Give a line an ID
function giveLineID(lineIndex,id){
editor.markText( {line:lineIndex,ch:0},{line:lineIndex,ch:line.length},{shapeID : id});
}
// Retrieve an ID from a line
function retrieveID(lineIndex){
var line = myCodeMirror.getLine(lineIndex);
var marks = cm.findMarks({line:lineIndex,ch:0},{line:lineIndex,ch:line.length});
marks.forEach(mark => {
if (mark.hasOwnProperty("shapeID")){
return mark["shapeID"];
}
});
}
We have a corporate content management system that allows for rich text editing/html markup, but does not allow for head elements or style sheets to be uploaded, attached, or used in any way. It provides some rich text editing controls and also access to the source html, but just for the html fragment -- there is no head, no body. We also have no access the whole system that presents these bits of markup on the page. The only way to style the content is through inline style attributes on the elements. It is best, it isn't pretty, but that is what we have and I'm trying to make the best of a bad situation.
We also have high standards for visual presentation and would like to be able to quickly produce and modify/update content and keep it looking nice. It is difficult to correctly apply formatting using the system. For anybody who has tried to markup anything more than a paragraph or two with an RTE, you probably know what I mean. It seems like we should have a different system, but has anybody worked for a large company before? Just sayin.
We do have access to another location where we could "author" and "store" actual styled content and then "compile it" for copypasta into the other system. In other words, we could author/design using css and best practices and then we could run some code that could convert those element, class, and id formatting into inline styles.
I did my research and found this thread which also lead me to this code.
These both are very helpful in exploring solutions, but I've run into an issue. These solutions use the javascript getComputedStyle() method. There are some other options for properties to only look at other properties or to be recursive on the children of the element provide, but basically it boils down to this. (Since getComputeStyle returns an object and not an array, there is also a prototype/polyfill to allow iterating over an object with forEach, but none of that is part of the issue I'm facing.)
const computedStyle = getComputedStyle(element);
computedStyle.forEach(property => {
element.style[property] = computedStyle.getPropertyValue(property);
});
This works well for css attributes like font-size:24px or margin:0 15px. The issue I'm running into are when I'm using units other than px. For example, if I'm trying to make something that has width:50%. getComputedStyle() converts the 50% to the actual number of pixels that 50% is currently using.
In the notes section of the MDN web docs I see that this is expected behavior. Although I'm not quite clear on what that last line means.
...An example difference between pre- and post-layout values includes the
resolution of percentages for width or height, as those will be
replaced by their pixel equivalent only for used values.
So what I'm trying to do is convert something like this
.container{width:50%;}
<div class="container">
into something like this
<div class="container" style="width:50%">
Does anyone know of a way to complete this type of transformation?
PS: If it matters we'll be using the more basic attributes in our css -- no transitions, grid, prefixing, etc. We still need to support IE 11 -- if that tells you anything. We won't need to account for every edge case or browser. Just some basic stuff so that all our H1 look the same.
Couldn't find any way to do this using the built in getComputedStyle(). It also returned too many properties that I wasn't interested in. So I came up with a different approach. Basically to use the same function to loop through an element (and maybe all its children elements) and the use Element.matches() to get all the css rules that apply to the element and apply the properties as they were specified in the stylesheet.
I modified this answer a bit to get the rules from the stylesheet.
Has the added benefit that we can pull either from all the document stylesheets or just from a specific one that is needed for preparing the code to go into our content management systems's rich text editor.
function applyInline(element, recursive = true) {
if (!element) {
throw new Error("No element specified.");
}
const matches = matchRules(element);
// we need to preserve any pre-existing inline styles.
var srcRules = document.createElement(element.tagName).style;
srcRules.cssText = element.style.cssText;
matches.forEach(rule => {
for (var prop of rule.style) {
let val = srcRules.getPropertyValue(prop) || rule.style.getPropertyValue(prop);
let priority = rule.style.getPropertyPriority(prop);
element.style.setProperty(prop,val,priority);
}
});
if (recursive) {
element.children.forEach(child => {
applyInline(child, recursive);
});
}
}
function matchRules(el, sheets) {
sheets = sheets || document.styleSheets;
var ret = [];
for (var i in sheets) {
if (sheets.hasOwnProperty(i)) {
var rules = sheets[i].rules || sheets[i].cssRules;
for (var r in rules) {
if (el.matches(rules[r].selectorText)) {
ret.push(rules[r]);
}
}
}
}
return ret;
}
I am trying to use ngGrid to make somewhat of a "tree-control" which I can build dynamically by calling API's. ngGrid allows for grouping on rows, yet the nature of it requires that all rows be present at the beginning. This is unfortunate for the fact that an API to pull back all generation data for a File Integrity Monitoring system would be insanely slow and stupid. Instead, I wish to build the "tree" dynamically on the expansion of each generation.
I am trying to inject children (ngRows) into a group-row (ngAggregate) on a callback, yet I do not think that I am calling the correct constructor for the ngRows for the fact that the rows are ignored by the control
Through the use of the aggregateTemplate option on the gridOptions for ngGrid, I have been able to intersept the expansion of a group quite easily.
(maybe not easily, but still)
I've replaced the ng-click of the default template:
ng-click="row.toggleExpand()"
with:
ng-click="$parent.$parent.rowExpanded(row)"
I know that it's a bit of a hack, but we can get to that later. For now, it gets the job done.
The way that I discovered how to work my way up the $scope to my rowExpanded function was by setting a breakpoint in ngGrid's "row.toggleExpand" function and calling it from the template as so:
ng-click="row.toggleExpand(this)"
Once I retrieve the group I want, I call an API to get the children for said group. I then need to make the return as children of the row. I decided to do this by calling ngGrid's ngRow factory:
row.children = [];
for(var i = 0; i < childData.length; i++)
{
row.children[row.children.length] = row.rowFactory.buildEntityRow(childData[i], i);
}
row.toggleExpand();
... yet this does not appear to be working. The rows are not showing up after I do the expand! Why won't my rows show up?
Here's my current Plunker!
By the way
I've placed a debugger statement within the group-expand callback. As long as you have your debugger open, you should catch a breakpoint on the expansion of a group.
Thanks everybody!
I found my answer, I'm an idiot....
I got this control working, and then realized that it was a total hack, that I could have used the control the way it was meant to be used and it would have worked much better, had much better work-flow, and it would have saved me an entire day of development. If you are wondering how you use the control this way, the answer is that you don't.
I got the stupid thing to work by updating my data structure after the round trip and forcing the grid to refresh, pretty obvious. I had to set the grid options so that groups were always expanded and I had to control the collapser icon logic myself, outside of ngGrid. I never called row.toggleExpand. I also hid any rows with null values by a function call within an ng-if on my rowTemplate. After all that was said and done, I put my foot in my mouth.
I'm very new to SVG (using D3.js to call everything). Recently, I just came into a huge limitation with a project I am working on. I want to be able to make "g" classes for each category of data I am working with. Unfortunately, I am getting my data from an XML file that only connects data in one way (ex: person1 ---> person2, but not person2 ---> person1). What I would like to be able to do is to put each shape generated from my data in the root class AND the class it is connecting with. If I could add this shape to two or more classes (such as g class = person1 and person2), that would be the fastest solution I believe...But is something like this possible? Can I set an SVG shape to two or more classes? Or will it overwrite it as I define new ones.
I really hope someone can understand what I am asking. It is kind of hard to verbalize my problem without giving away every detail of my final project.
Yes, you can set multiple classes. For example,
<g class="person1 person2">
Or, in D3:
g.attr("class", "person1 person2");
One more note: If you're calling a function from within a file:
.attr("class",function(d) { return d.person1+" "+ d.person2;} )
I have a fairly large map consisting of Raphael paths which im trying to make accessible to some jquery ajax scripts to be populated. I've tried to add an ID or anything to make it accessible from jquery in an organized fashion.
As im new to Raphael I cant figure out a good way of achieving this.
I've tried using .data() to add an ID for each dot with say "seat_1","seat_2" and so on but been unsuccessful so far.
how would I go about organizing this code so I could manipulate it by looping?
I realize that its a fairly open ended question but any suggestion is much appreciated
Demo here: http://www.sam-sys.in/demo/pushparaj/ticketreservation/?page_id=203
var path_gs = rsr.path("M5.834,698.336c-3.217,0-5.833,2.615-5.833,5.831 c0,3.215,2.616,5.833,5.833,5.833c3.219,0,5.835-2.618,5.835-5.833C11.669,700.951,9.053,698.336,5.834,698.336");
path_gs.attr({"clip-path": 'url(#SVGID_2_)',fill: '#777675',parent: 'group_a','stroke-width': '0','stroke-opacity': '1'}).data('id', 'path_gs');
which generates
<path style="stroke-opacity: 1; " fill="#008000" stroke="#000000" d="M5.834,698.336C2.6169999999999995,698.336,0.0009999999999994458,700.951,0.0009999999999994458,704.167C0.0009999999999994458,707.3820000000001,2.6169999999999995,710,5.834,710C9.052999999999999,710,11.669,707.382,11.669,704.167C11.669,700.951,9.053,698.336,5.834,698.336" stroke-width="0" stroke-opacity="1"></path>
Well, the way I'm doing this is the following. First I write all the paths in an object, for example:
var paths = {
path1: 'the paths coordinates',
path2: 'the paths coordinates',
path3: 'the paths coordinates',
}
Then you just loop trough all the paths, seting the coordinates for each path and giving them an ID (this is an internal Raphael's ID):
for(path in paths){
var newpath = paper.path(paths[path]);
newpath.attr({options})
newpath.id = path;
}
Now, if you want to get one of this elements you can use the next Raphael feature:
var thisPath = paper.getById('path1');
This way you can use the path on any of the Raphael methods. So, if you need to get the node in the dome you can do the following:
var node = thisPath.node
But if you need to animate the path, you better use the Raphael's animate method, or if you need to change attibutes the attr method.
thisPath.animate(.....)
If you need to apply some change to all the paths, you can use:
paper.forEach(function(thisArg))
you need to pass the function to run on each element and thisArg reference the element on each iteration
And maybe you want to take a look to the Raphael's sets wich can be useful for use methods on groups of elements. If you need any help using this features just let me know and I'll do my best to help you out. Bye!
You could just push them to an array :
var pathArray = new Array();
var path_gs = rsr.path("path coords");
pathArray.push(path_gs);
Then loop through pathArray.
Another option is grouping them in sets.