convert xml to json while keeping code working - html

I have a xml file, and an html file associated with it.
the html file takes the xml elements and outputs a tree with hyperlinks and pictures for each node type included. how can I convert the code to work for JSON instead of XML. this is the code and the xml
I added a function to attr elements so it opens a table when I click on it:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
url: "converted14.json",
dataType: 'json',
success: function(tree) {
traverse($('#treeView'), tree)
$('#treeView li:has(li)').addClass("Max").click(function(e) {
$(this).toggleClass("Max Min");
$(this).children("ul").toggle();
e.stopPropagation();
})
$("[href]").addClass("Blue").click(function(e){
$(this).toggleClass("Purple");
e.stopPropagation();
window.location.href = $(this).attr("href")
})
$('#treeView li').click(function(g) {
var mytree2 = $(this);
mytree2.children('li').toggle();
g.stopPropagation();
})
}
})
});
function traverse(node, o) {
for (var i in o) {
if(i == "__text" || i == "_href" || i == "_attr")
continue;
if (o[i] !== null && typeof(o[i])=="object") {
if(o[i].__text){
var ul = $("<ul>").appendTo(node)
var node=$('<li>').appendTo(ul)
if(o[i]._href){
var n = $("<span>").appendTo(node)
$(n).text(o[i].__text).attr("href", o[i]._href)
}
else{
$(node).text(o[i].__text)
}
if(o[i]._attr){
var n = $("<span>").appendTo(node)
$(n).text(o[i]._attr)
$(n).hide()
$(n).parent().click(function(g){
$(n).toggle().addClass("Table")
})
}
}
traverse(node,o[i]);
}
else{
var ul = $("<ul>").appendTo(node)
if(o[i].__text){
var li = $('<li>' + o[i].__text + '<\/li>').appendTo(ul)
if(o[i]._href){
var n = $("<span>").appendTo(node)
$(n).text(o[i].__text).attr("href", o[i]._href)
}
}else{
$('<li>' + o[i] + '</li>').appendTo(ul)
}
}
}
}
</script>
<style type="text/css">
#treeView li {
list-style: none;
}
#treeView ul {
padding-left: 1em;
}
#treeView b {
padding-right: 1em;
}
.Min {
background: URL("http://i1341.photobucket.com/albums/o747/Mike_Younes/plus_zps8o4adn0e.gif") no-repeat;
padding-left: 15px;
}
.Blue {
text-decoration: underline;
color: Blue;
}
.Purple {
text-decoration: underline;
color: Purple;
}
.Max {
background: URL("http://i1341.photobucket.com/albums/o747/Mike_Younes/minus_zpsk0jlvbaa.gif") no-repeat;
padding-left: 15px;
}
li {
background: URL("http://i1341.photobucket.com/albums/o747/Mike_Younes/bullet_zpsblghj3ip.gif") no-repeat;
padding-left: 15px;
}
.Table {
background-color : yellow;
border: 1px solid black;
border-collapse: collapse;
width: 300px;
height: 100px;
padding: 50px;
box-sizing: border-box;
position: absolute;
right: 100px;
top: 50px;
}
.MainStation.Max {
background: URL("http://i1341.photobucket.com/albums/o747/Mike_Younes/station_zpswxz6gpqe.jpg") no-repeat;
background-size: 15px 15px;
cursor: pointer;
padding-left: 15px;
}
.Substation.Max {
background: URL("http://i1341.photobucket.com/albums/o747/Mike_Younes/sub_zpsspl8dckt.jpg") no-repeat;
background-size: 15px 15px;
cursor: pointer;
padding-left: 15px;
}
.ControlExpandable.Max {
background: URL("http://i1341.photobucket.com/albums/o747/Mike_Younes/control_zpsrfvdzyzp.jpg") no-repeat;
background-size: 15px 15px;
cursor: pointer;
padding-left: 15px;
}
.PushButtonExpandable.Max {
background: URL("http://i1341.photobucket.com/albums/o747/Mike_Younes/pusbutton_zpsspjfsfsp.jpg") no-repeat;
background-size: 15px 15px;
cursor: pointer;
padding-left: 15px;
}
.Control {
background: URL("http://i1341.photobucket.com/albums/o747/Mike_Younes/control_zpsrfvdzyzp.jpg") no-repeat;
background-size: 15px 15px;
cursor: pointer;
padding-left: 15px;
}
.PushButton {
background: URL("http://i1341.photobucket.com/albums/o747/Mike_Younes/pusbutton_zpsspjfsfsp.jpg") no-repeat;
background-size: 15px 15px;
cursor: pointer;
padding-left: 15px;
}
.ElectricStation.Max {
background: URL("http://i1341.photobucket.com/albums/o747/Mike_Younes/electrical-safety-symbol_zpssdvscba0.gif") no-repeat;
background-size: 15px 15px;
cursor: pointer;
padding-left: 15px;
}
</style>
<title>treeView</title>
</head>
<body>
<div id="treeView">
</div>
</body>
</html>
and heres the json file again. only 1 line added to test the attr:
{
"MAIN": {
"MainStation": [
{
"Substation": [
{
"Control": "Control1\n",
"ControlExpandable": {
"MiniControl": [
"MiniControl1",
"MiniControl2"
],
"__text": "Control2"
},
"PushButton": {
"__text": "PushButton1",
"_attr": "success\nDate:17july"
},
"_href": "http://google.com",
"__text": " Substation1\n\n\n\n\n\n\n"
},
{
"ControlExpandable": {
"MiniControl": [
"MiniControl1",
"MiniControl2"
],
"__text": "Control1"
},
"Control": "Control2",
"PushButton": "PushButton1",
"__text": " Substation2\n\n\n\n\n\n\n"
},
{
"Control": [
"Control1",
"Control2",
"Control3",
"Control4"
],
"__text": " Substation3\n\n\n\n\n\n\n\n\n"
},
{
"PushButton": [
"PushButton1",
"PushButton2"
],
"__text": " Substation4\n\n\n\n\n"
}
],
"__text": " Mainstation1\n\n\n\n\n\n\n\n\n"
},
{
"Substation": [
{
"Control": [
"Control1",
"Control2"
],
"PushButton": "PushButton1",
"__text": " Substation1\n\n\n\n\n\n\n"
},
{
"ControlExpandable": {
"MiniControl": [
"MiniControl1",
"MiniControl2"
],
"__text": "Control1"
},
"Control": "Control2",
"PushButtonExpandable": {
"MiniPushButton": [
"MiniPushButton1",
"MiniPushButton2"
],
"__text": "PushButton1"
},
"__text": " Substation2\n\n\n\n\n\n\n"
}
],
"__text": " Mainstation2\n\n\n\n\n"
}
],
"ElectricStation": {
"Substation": [
{
"Control": [
"Control1",
"Control2"
],
"PushButton": "PushButton1",
"__text": " Substation1\n\n\n\n\n\n\n"
},
{
"ControlExpandable": {
"MiniControl": [
"MiniControl1",
"MiniControl2"
],
"__text": "Control1"
},
"Control": "Control2",
"PushButtonExpandable": {
"MiniPushButton": [
"MiniPushButton1",
"MiniPushButton2"
],
"__text": "PushButton1"
},
"__text": " Substation2\n\n\n\n\n\n\n"
}
],
"__text": " ElectricStation1\n\n\n\n\n"
},
"__text": " MAIN\n\n\n\n\n\n\n"
}
}

I have tried writing the parser, Hope it helps.
The Json, is the same file that you have posted
The complete Javascript code,
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
url: "test.json",
dataType: 'json',
success: function(tree) {
traverse($('#treeView'), tree)
$('#treeView li:has(li)').addClass("Max").click(function(e) {
$(this).toggleClass("Max Min");
$(this).children("ul").toggle();
e.stopPropagation();
})
$("[href]").addClass("Blue").click(function(e){
$(this).toggleClass("Purple");
e.stopPropagation();
window.location.href = $(this).attr("href")
})
$('#treeView li').click(function(g) {
var mytree2 = $(this);
mytree2.children('li').toggle();
g.stopPropagation();
})
}
})
});
function traverse(node, o) {
for (var i in o) {
if(i == "__text" || i == "_href")
continue;
if (o[i] !== null && typeof(o[i])=="object") {
if(o[i].__text){
var ul = $("<ul>").appendTo(node)
var node=$('<li>').appendTo(ul)
if(o[i]._href){
var n = $("<span>").appendTo(node)
$(n).text(o[i].__text).attr("href", o[i]._href)
}else{
$(node).text(o[i].__text)
}
}
traverse(node,o[i]);
}
else{
var ul = $("<ul>").appendTo(node)
if(o[i].__text){
var li = $('<li>' + o[i].__text + '<\/li>').appendTo(ul)
if(o[i]._href){
var n = $("<span>").appendTo(node)
$(n).text(o[i].__text).attr("href", o[i]._href)
}
}else{
$('<li>' + o[i] + '</li>').appendTo(ul)
}
}
}
}
</script>
Also i have changed the treeView unordered list to div.
<body>
<div id="treeView">
</div>
</body>
[EDIT]
function traverse(data) {
if (typeof(data) == 'object') {
var ul = $('<ul>');
for (var i in data) {
if(i == "__text" || i == "_href" || i == "_attr")
continue;
if(data[i].__text){
ul.append($('<li>').text(data[i].__text).append(traverse(data[i])));
}
else{
ul.append(traverse(data[i]));
}
}
return ul;
} else {
var textNode = $('<li>').text(data);
return textNode;
}
}
also change
traverse($('#treeView'), tree)
to
$('#treeView').append(traverse(tree))
in the ajax success function. I haven't handled for the attr and http. you can add it in the same way as the previous function.

Related

Change color of absolutely positioned element when overlapping with another

I am trying to build a component where you can visualize on what section are you in and allows you to easily move sections on the page.
My page is structured like this
<SectionManager /> // the absolutely positioned element
<Navbar />
<Menu />
<Section1 />
<Section2 />
<Section3 />
{...}
I want to have every even numbered section to have a black background and the others to have a white background. Now I want the text inside my SectionManager component to be white when overlapping a black background and black when overlapping a white background.
Here is a photo:
My component is the one on the left. And when you scroll down to the black section I want just the about me text and the circle after that to turn white.
Sorry if this is a stupid question by I searched for hours and did not find anything. I tried mix-blend-mode but it did not work.
Here the code for my component:
const SectionManager: React.FC = () => {
const globalState = React.useContext(MyContext);
const observerCallback = (entries: IntersectionObserverEntry[]) => {
...
};
const observerOptions = React.useMemo(
...
);
React.useEffect(() => {
const observer = new IntersectionObserver(...);
globalState.currentSections.forEach((section: HTMLElement) => {
observer.observe(section);
});
}, []);
const sections = [
{
text: "Hello!",
},
{
text: "about me",
},
{
text: "work i did",
},
{
text: "contact",
},
];
return (
<div className={styles.sectionManager}>
{sections.map((section, sectionID) => (
<>
{sectionID > 0 && (
<div className={styles.sectionManager_separator}></div>
)}
<div
className={
sectionID === globalState.activeSectionId
? `${styles.sectionManager_item} ${styles.sectionManager_itemActive}`
: styles.sectionManager_item
}
>
<p>{section.text}</p>
</div>
</>
))}
</div>
);
};
export default SectionManager;
here is the scss file:
.sectionManager {
position: fixed;
z-index: 100;
right: 30px;
top: 50%;
display: flex;
flex-direction: column;
align-items: flex-end;
transform: translateY(-50%);
&_separator {
width: 1px;
height: 25px;
background: $text-secondary-dark;
margin-right: 7px;
}
&_itemActive {
&::after {
background-color: $text-primary-light !important;
transform: scale(1) !important;
}
p {
color: $text-primary-light !important;
transform: scaleX(1) !important;
}
}
&_item {
mix-blend-mode: difference;
display: flex;
align-items: center;
font-size: 1rem;
margin-top: 5px;
cursor: pointer;
background: transparent;
#include transition();
&:hover {
&::after {
background-color: $text-primary-light;
transform: scale(1);
}
p {
transform: scaleX(1);
color: $text-primary-light;
}
}
p {
margin: 0;
transform: scaleX(0);
transform-origin: right;
color: $text-secondary-light;
#include transition();
}
&::after {
content: "";
width: 15px;
height: 15px;
border-radius: 999999px;
margin-left: 10px;
transform: scale(0.9);
background: $text-secondary-dark;
#include transition();
}
}
}
And for the section background I am not doing anything fancy, I am just setting a background-color property on there.
Thank you in advance!
Edit:
I want something similar to that. The design is in figma.
I solved the issue!
I ended up getting all the sections on my page using querySelector and using an IntersectionObserver to get the section that is in viewPort and get its background color, then passing the background color to my component using data-section-bg.
Here is the whole component code:
const SectionManager: React.FC = () => {
const [currentSectionBg, setCurrentSectionsBg] =
React.useState<string>("#fff");
const globalState = React.useContext(MyContext);
const observerCallback = (entries: IntersectionObserverEntry[]) => {
// other observer ...
};
const sectionColorObserverCallback = (
entries: IntersectionObserverEntry[]
) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0.25) {
const sectionBgColor = (
document.getElementById(entry.target.id) as HTMLElement
).style.backgroundColor;
console.log(sectionBgColor);
setCurrentSectionsBg(sectionBgColor);
}
});
};
const observerOptions = React.useMemo(
() => ({
root: null,
rootMargin: "0px",
threshold: 0.25,
}),
[]
);
React.useEffect(() => {
const observer = new IntersectionObserver(
// other observer...
);
globalState.currentSections.forEach((section: HTMLElement) => {
// other observer...
});
// Detect Section Color Observer
const allSections = document.querySelectorAll("section");
if (!allSections) return;
const sectionColorObserver = new IntersectionObserver(
sectionColorObserverCallback,
observerOptions
);
allSections.forEach((section, sectionId) => {
section.id = `SECTION_${sectionId}`;
section.style.backgroundColor = sectionId % 2 === 0 ? "#fff" : "#000";
sectionColorObserver.observe(section);
});
}, []);
const sections = [
{
text: "Hello!",
},
{
text: "about me",
},
{
text: "work i did",
},
{
text: "contact",
},
];
return (
<motion.div
initial={{ x: 150, opacity: 0 }}
animate={{
x: 0,
y: "-50%",
opacity: 1,
transition: {
duration: 1,
delay: 0.8,
ease: defaultAnimationEasing,
},
}}
className={styles.sectionManager}
>
{sections.map((section, sectionID) => (
<>
{sectionID > 0 && (
<div className={styles.sectionManager_separator}></div>
)}
<div
data-section-bg={currentSectionBg}
className={
sectionID === globalState.activeSectionId
? `${styles.sectionManager_item} ${styles.sectionManager_itemActive}`
: styles.sectionManager_item
}
>
<p>{section.text}</p>
</div>
</>
))}
</motion.div>
);
};
export default SectionManager;
Here is what I added to my scss File:
[data-section-bg="rgb(0, 0, 0)"] {
&:hover {
&::after {
background: $text-primary-dark !important;
}
p {
color: $text-primary-dark;
}
}
p {
color: $text-primary-dark;
}
}
[data-section-bg="rgb(255, 255, 255)"] {
&:hover {
&::after {
background: $text-primary-light !important;
}
p {
color: $text-primary-light;
}
}
p {
color: $text-primary-light;
}
}
&_itemActive[data-section-bg="rgb(255, 255, 255)"] {
&::after {
background-color: $text-primary-light !important;
}
}
&_itemActive[data-section-bg="rgb(0, 0, 0)"] {
&::after {
background-color: $text-primary-dark !important;
}
}

Why width includes border when box-sizing is content-box?

I have this html file
<html lang="en" class=""><head>
<meta charset="UTF-8">
<title>CodePen Demo</title>
<meta name="robots" content="noindex">
<link rel="shortcut icon" type="image/x-icon" href="https://static.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico">
<link rel="mask-icon" type="" href="https://static.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg" color="#111">
<link rel="canonical" href="https://codepen.io/hardkoded/pen/VwjvWBm">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
<style class="INLINE_PEN_STYLESHEET_ID">
body {
font: 14px "Century Gothic", Futura, sans-serif;
margin: 20px;
}
ol, ul {
padding-left: 30px;
}
.board-row:after {
clear: both;
content: "";
display: table;
}
.status {
margin-bottom: 10px;
}
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
padding: 0;
text-align: center;
width: 34px;
}
.square:focus {
outline: none;
}
.kbd-navigation .square:focus {
background: #ddd;
}
.game {
display: flex;
flex-direction: row;
}
.game-info {
margin-left: 20px;
}
</style>
<script src="https://static.codepen.io/assets/editor/iframe/iframeConsoleRunner-7f4d47902dc785f30dedcac9c996b9f31d4dfcc33567cc48f0431bc918c2bf05.js"></script>
<script src="https://static.codepen.io/assets/editor/iframe/iframeRefreshCSS-e03f509ba0a671350b4b363ff105b2eb009850f34a2b4deaadaa63ed5d970b37.js"></script>
<script src="https://static.codepen.io/assets/editor/iframe/iframeRuntimeErrors-29f059e28a3c6d3878960591ef98b1e303c1fe1935197dae7797c017a3ca1e82.js"></script>
</head>
<body class="mouse-navigation">
<div id="errors" style="
background: #c00;
color: #fff;
display: none;
margin: -20px -20px 20px;
padding: 20px;
white-space: pre-wrap;
"></div>
<div id="root"><div class="game"><div class="game-board"><div><div class="board-row"><button class="square"></button><button class="square"></button><button class="square"></button></div><div class="board-row"><button class="square"></button><button class="square"></button><button class="square"></button></div><div class="board-row"><button class="square"></button><button class="square"></button><button class="square"></button></div></div></div><div class="game-info"><div id="status">Next player: X</div><ol><li><button>Go to game start</button></li></ol></div></div></div>
<script>
window.addEventListener('mousedown', function(e) {
document.body.classList.add('mouse-navigation');
document.body.classList.remove('kbd-navigation');
});
window.addEventListener('keydown', function(e) {
if (e.keyCode === 9) {
document.body.classList.add('kbd-navigation');
document.body.classList.remove('mouse-navigation');
}
});
window.addEventListener('click', function(e) {
if (e.target.tagName === 'A' && e.target.getAttribute('href') === '#') {
e.preventDefault();
}
});
window.onerror = function(message, source, line, col, error) {
var text = error ? error.stack || error : message + ' (at ' + source + ':' + line + ':' + col + ')';
errors.textContent += text + '\n';
errors.style.display = '';
};
console.error = (function(old) {
return function error() {
errors.textContent += Array.prototype.slice.call(arguments).join(' ') + '\n';
errors.style.display = '';
old.apply(this, arguments);
}
})(console.error);
</script>
<script src="https://static.codepen.io/assets/common/stopExecutionOnTimeout-157cd5b220a5c80d4ff8e0e70ac069bffd87a61252088146915e8726e5d9f147.js"></script>
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script id="INLINE_PEN_JS_ID">
function Square(props) {
return (
React.createElement("button", { className: "square", onClick: props.onClick },
props.value));
}
class Board extends React.Component {
renderSquare(i) {
return (
React.createElement(Square, {
value: this.props.squares[i],
onClick: () => this.props.onClick(i) }));
}
render() {
return (
React.createElement("div", null,
React.createElement("div", { className: "board-row" },
this.renderSquare(0),
this.renderSquare(1),
this.renderSquare(2)),
React.createElement("div", { className: "board-row" },
this.renderSquare(3),
this.renderSquare(4),
this.renderSquare(5)),
React.createElement("div", { className: "board-row" },
this.renderSquare(6),
this.renderSquare(7),
this.renderSquare(8))));
}}
class Game extends React.Component {
constructor(props) {
super(props);
this.state = {
history: [
{
squares: Array(9).fill(null) }],
stepNumber: 0,
xIsNext: true };
}
handleClick(i) {
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
const squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? "X" : "O";
this.setState({
history: history.concat([
{
squares: squares }]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext });
}
jumpTo(step) {
this.setState({
stepNumber: step,
xIsNext: step % 2 === 0 });
}
render() {
const history = this.state.history;
const current = history[this.state.stepNumber];
const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
React.createElement("li", { key: move },
React.createElement("button", { onClick: () => this.jumpTo(move) }, desc)));
});
let status;
if (winner) {
status = "Winner: " + winner;
} else {
status = "Next player: " + (this.state.xIsNext ? "X" : "O");
}
return (
React.createElement("div", { className: "game" },
React.createElement("div", { className: "game-board" },
React.createElement(Board, {
squares: current.squares,
onClick: i => this.handleClick(i) })),
React.createElement("div", { className: "game-info" },
React.createElement("div", { id: "status" }, status),
React.createElement("ol", null, moves))));
}}
// ========================================
ReactDOM.render(React.createElement(Game, null), document.getElementById("root"));
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]];
for (let i = 0; i < lines.length; i++) {if (window.CP.shouldStopExecution(0)) break;
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}window.CP.exitedLoop(0);
return null;
}
//# sourceURL=pen.js
</script>
</body></html>
It's a tic tac toe game from this codepen link https://codepen.io/sophiebits/pen/qNOZOP (I've modified it a little bit).
When I used the inspect utility of Chrome to select one square, and looked at the computed css box model, it says the content of the square is 32px. But in the css portion of the html file, I clearly set the width of the square to be 34px, shouldn't the computed css box model have content width of 34px?
I know that the default value of box-sizing is content-box, so the width property should contain neither border nor padding. That's why I have this question.
Buttons (and some other items) use a box-sizing property "border-box" instead of "content-box"; this explains the different behavior.
Documentation for reference: https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing
You can change this in css using:
.square{
box-sizing: content-box; /* border-box */
}
See this very simple JSFiddle: https://jsfiddle.net/ue5f164h/
<div class="box"></div>
.box {
background: blue;
border: 5px solid red;
width: 20px;
height: 20px;
}
For me, the width is correct!
You should give a smaller reproducible example in your question so that it's easier for us to debug what's gone wrong.
But as the other answer points out, the problem is that some elements have a different default box-sizing value.
Basically the way box-sizing works is that, it determines if element width should include border or not.
For example -> If you have a div with width of 300px
By using box-sizing: border-box like so
div {
width: 300px;
height: 100px;
border: 10px solid blue;
box-sizing: border-box;
}
Then the width of this div element will always be 300px -> 20px of border and 280px of content -> by adding padding or changing border size you are changing the size of the content
If you use box-sizing: content-box like so:
div {
width: 300px;
height: 100px;
border: 10px solid red;
box-sizing: content-box;
}
The width of the div element will now be 320px -> 300px of content + 20px of border width.
So if you want your element to always have width or height of lets say 50px you would use box-sizing: border-box

Error: [$injector:unpr] Unknown provider: treeGridProvider (Angular Grid Tree table is not working)

I want to display grid tree table like http://khan4019.github.io/tree-grid-directive/test/treeGrid.html
I have already installed directive using
$ npm install angular-bootstrap-grid-tree
grifCtrl.js
angular.module('app').controller('grifCtrl', ['$scope','treeGrid',function($scope,treeGrid){
$scope.tree_data = [
{Name:"USA",Area:9826675,Population:318212000,TimeZone:"UTC -5 to -10",
children:[
{Name:"California", Area:423970,Population:38340000,TimeZone:"Pacific Time",
children:[
{Name:"San Francisco", Area:231,Population:837442,TimeZone:"PST"},
{Name:"Los Angeles", Area:503,Population:3904657,TimeZone:"PST"}
]
},
{Name:"Illinois", Area:57914,Population:12882135,TimeZone:"Central Time Zone",
children:[
{Name:"Chicago", Area:234,Population:2695598,TimeZone:"CST"}
]
}
]
},
{Name:"Texas",Area:268581,Population:26448193,TimeZone:"Mountain"}
];
}
]);
grid.html
<div ng-controller="grifCtrl">
<tree-grid tree-data="tree_data"></tree-grid>
</div>
But it is not working and giving below
angular.js:13920 Error: [$injector:unpr] Unknown provider: treeGridProvider <- treeGrid <- grifCtrl
Also this directive will automatically display table structure right????
Anyone pls suggest
inject your treeGrid inside the function as well.
.controller('grifCtrl', ['$scope','treeGrid',function($scope, treeGrid) { ..}
should work.
Updated:
And before that make sure you injected in module array also:
angular.module('app', [..., 'treeGrid'])
Used this example and completed report, check this link https://codepen.io/sliiice/pen/GurpF
HTML:
<h1>
Tree Table and Checkbox with AngularJS
</h1>
<div class="wrapper" ng-app="testApp" ng-controller="treeTable">
<table class="table-nested">
<thead>
<tr>
<th class="cell-input">
<input ng-checked="(list | selected).length == list.length" ng-click="toggleAllCheckboxes($event)" type="checkbox" />
</th>
<th>
Name
</th>
<th class="cell-members">
Members
</th>
<th>
Title
</th>
</tr>
</thead>
<tbody ng-class="{opened: item.opened}" ng-include="'table_tree.html'" ng-repeat="item in list"></tbody>
</table>
<script id="table_tree.html" type="text/ng-template">
<tr ng-class="{parent: item.children}" ng-init="parentScope = $parent.$parent; initCheckbox(item, parentScope.item)">
<td class="cell-input">
<input ng-change="toggleCheckbox(item, parentScope)" ng-model="item.selected" type="checkbox" />
</td>
<td class="cell-name" ng-click="item.opened = !item.opened">
<div class="indent" style="padding-left: {{15*level}}px"></div>
{{item.name}}
</td>
<td class="cell-members">
{{item.children.length}}
</td>
<td>
{{item.title}}
</td>
</tr>
<tr class="children" ng-if="item.children && item.children.length > 0">
<td colspan="4">
<table>
<tbody ng-class="{opened: item.opened}" ng-include="'table_tree.html'" ng-init="level = level + 1" ng-repeat="item in item.children"></tbody>
</table>
</td>
</tr>
</script>
</div>
JS:
(function() {
var app, list;
list = [
{
name: 'Developer',
opened: true,
children: [
{
name: 'Front-End',
children: [
{
name: 'Jack',
title: 'Leader'
},
{
name: 'John',
title: 'Senior F2E'
},
{
name: 'Jason',
title: 'Junior F2E'
}
]
},
{
name: 'Back-End',
children: [
{
name: 'Mary',
title: 'Leader'
},
{
name: 'Gary',
title: 'Intern'
}
]
}
]
},
{
name: 'Design',
children: [
{
name: 'Freeman',
title: 'Designer'
}
]
},
{
name: 'S&S',
children: [
{
name: 'Nikky',
title: 'Robot'
}
]
}
];
app = angular.module('testApp', []).controller('treeTable', [
'$scope',
'$filter',
function($scope,
$filter) {
$scope.list = list;
$scope.toggleAllCheckboxes = function($event) {
var i,
item,
len,
ref,
results,
selected;
selected = $event.target.checked;
ref = $scope.list;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
item = ref[i];
item.selected = selected;
if (item.children != null) {
results.push($scope.$broadcast('changeChildren',
item));
} else {
results.push(void 0);
}
}
return results;
};
$scope.initCheckbox = function(item,
parentItem) {
return item.selected = parentItem && parentItem.selected || item.selected || false;
};
$scope.toggleCheckbox = function(item,
parentScope) {
if (item.children != null) {
$scope.$broadcast('changeChildren',
item);
}
if (parentScope.item != null) {
return $scope.$emit('changeParent',
parentScope);
}
};
$scope.$on('changeChildren',
function(event,
parentItem) {
var child,
i,
len,
ref,
results;
ref = parentItem.children;
results = [];
for (i = 0, len = ref.length; i < len; i++) {
child = ref[i];
child.selected = parentItem.selected;
if (child.children != null) {
results.push($scope.$broadcast('changeChildren',
child));
} else {
results.push(void 0);
}
}
return results;
});
return $scope.$on('changeParent',
function(event,
parentScope) {
var children;
children = parentScope.item.children;
parentScope.item.selected = $filter('selected')(children).length === children.length;
parentScope = parentScope.$parent.$parent;
if (parentScope.item != null) {
return $scope.$broadcast('changeParent',
parentScope);
}
});
}
]);
app.filter('selected', [
'$filter',
function($filter) {
return function(files) {
return $filter('filter')(files,
{
selected: true
});
};
}
]);
}).call(this);
CSS:
#charset "UTF-8";
body {
font: 13px helvetica;
width: 80%;
margin: 40px auto;
background: #eee;
text-align: center;
}
table {
width: 100%;
table-layout: fixed;
border-collapse: collapse;
border-spacing: 0;
}
.table-nested {
background: #fff;
border: 2px solid #444;
text-align: left;
}
.table-nested th, .table-nested td {
padding: 0;
}
.table-nested th + th, .table-nested th + td, .table-nested td + th, .table-nested td + td {
padding-left: 5px;
}
.table-nested td {
border-top: 1px solid;
}
.table-nested td[colspan] {
border: none;
}
.table-nested .cell-input {
width: 20px;
border-right: 1px solid;
}
.table-nested .cell-members {
width: 100px;
}
.table-nested .indent {
display: inline-block;
}
.table-nested .parent > .cell-name {
cursor: pointer;
}
.table-nested .parent > .cell-name > .indent {
margin-right: 5px;
}
.table-nested .parent > .cell-name > .indent:before {
content: "";
font-family: FontAwesome;
display: inline-block;
-moz-transition: -moz-transform 0.3s;
-o-transition: -o-transform 0.3s;
-webkit-transition: -webkit-transform 0.3s;
transition: transform 0.3s;
}
.table-nested .children {
display: none;
}
.table-nested .opened > tr > .cell-name > .indent:before {
-moz-transform: rotate(90deg);
-ms-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
}
.table-nested .opened > .children {
display: table-row;
}

Polymer.lazyRegister destroy style while upgrading from, v1.2.3 to v1.4.0

i'm developping an application with Polymer.
My old version was v1.2.3.
I try since this morning to optimize my application about import using the importHref function to load some files on the fly.
I've found that there is a bug (https://github.com/Polymer/polymer/issues/3638) so I tried to update to the last version to have the bugfix (v1.7.0). After upgrading, i seems that some problems occurs with styling.
After some search, i've found that the problem is here when I update from v1.2.3 to v1.4.0 and using
window.Polymer.lazyRegister = true;
It adds some scrollbars (don't find why or where or how), shift some styles, .... and absolutely don't know why.
Does anyone have any idea?
thanks a lot
Edit : add before / after screenshot
edit : add code
index.html
<title>my app</title>
<script src="bower_components/lodash/lodash.js"></script>
<script src="lib/md5/md5.js"></script>
<script src="bower_components/page/page.js"></script>
<link rel="import" href="utils/behaviors/i18n/i18n.html">
<script>
// Setup Polymer options
window.Polymer = {
dom: 'shady',
lazyRegister: true
};
(function() {
'use strict';
var onload = function() {
if (!window.HTMLImports) {
document.dispatchEvent(
new CustomEvent('WebComponentsReady', {bubbles: true})
);
}
};
var webComponentsSupported = (
'registerElement' in document
&& 'import' in document.createElement('link')
&& 'content' in document.createElement('template')
);
if (!webComponentsSupported) {
var script = document.createElement('script');
script.async = true;
script.src = '/bower_components/webcomponentsjs/webcomponents-lite.min.js';
script.onload = onload;
document.head.appendChild(script);
} else {
onload();
}
})();
</script>
<link rel="import" href="bower_components/polymer/polymer.html">
<!-- not paste, but here are the polymer elements papaer-*, iron-*, ... -->
<!-- elements.html is just an external file with all my own elements -->
<link rel="import" href="elements/elements.html">
<link href="res/css/app.css" rel="stylesheet"/>
<style>
body {
margin: 0;
height: 100vh;
}
</style>
</head>
<body class="layout vertical">
<template is="dom-bind" id="myApp">
<my-application id="application"></my-application>
</template>
</body>
</html>
my-application.html
<dom-module id="my-application">
<style>
:host {
height : 100%;
}
iron-pages {
height : 100%;
}
#popin {
position: absolute;
display: none;
z-index: 100;
}
#mainCtn {
height: 100%;
width: 100%;
}
my-loader-synchronizer {
z-index: 100;
height: 100%;
width: 100%;
position: absolute;
top: 0px;
left: 0px;
}
</style>
<template>
<div id="mainCtn" on-click="_onMainCtnClick">
<iron-pages attr-for-selected="data-route" selected="{{route}}">
<my-login id="log" data-route="login"></my-login>
<paper-header-panel data-route="dashboard">
<my-header class="paper-header"></my-header>
<my-dashboard id="dashboard" class="fit"></my-dashboard>
</paper-header-panel>
</iron-pages>
<my-dialog id="dialog"></my-dialog>
<my-loader-synchronizer id="loaderSynchronizer" hidden$="{{!synchro}}"></my-loader-synchronizer>
<div id="popin"></div>
<my-dialog-selector id="dialogSelector"></my-dialog-selector>
</div>
<my-toast id="toast"></my-toast>
</template>
<link rel="import" href="../../utils/behaviors/storage/ramCache.html">
<link rel="import" href="../../utils/behaviors/session/session.html">
<link rel="import" href="../../utils/behaviors/communication/applicationCommunication.html">
<script>
Polymer({
is: 'my-application',
properties: {
"synchro" : {
"type" : Boolean,
"value" : false
},
"amIConnected" : {
"type" : Boolean,
"value" : false
},
animationConfig : {
type : Object,
value : function () {
return {
"fadeOut" : [{
name : 'fade-out-animation',
node : this.$$("#loaderSynchronizer")
}],
}
}
}
},
behaviors : [applicationCommunication, Polymer.NeonAnimationRunnerBehavior],
listeners : {
"neon-animation-finish": "fadeComplete"
},
ready: function ready() {
// all listeners are registered here
this.addEventListener("mousemove", this._onGlobalMouseMove.bind(this));
this._disconnectTimeout = setTimeout(this._disconnect.bind(this), 3600000);
},
_onGlobalMouseMove : function _onGlobalMouseMove (e) {
if (this.amIConnected == true) {
if (e.movementX != 0 || e.movementY != 0) {
clearTimeout(this._disconnectTimeout);
this._disconnectTimeout = setTimeout(this._disconnect.bind(this), 3600000);
}
}
},
_disconnect : function _disconnect () {
clearTimeout(this._disconnectTimeout);
session.getInstance().disconnect();
},
attached : function () {
page("/", function () {
this.route = "login";
}.bind(this));
page("/dashboard", function () {
this.amIConnected = true;
this.synchro = true;
this.$$("#loaderSynchronizer").synchronizeData();
this.$.dashboard.onShow();
this.route = "dashboard";
}.bind(this));
page({
hashbang: true
});
},
fadeComplete : function fadeComplete () {
this.synchro = false;
},
_closeLoaderSynchronizer : function _closeLoaderSynchronizer () {
this.cancelAnimation();
this.playAnimation("fadeOut");
},
});
</script>
</dom-module>
my-login.html
<dom-module id="my-login">
<style>
:host {
font-family : "Roboto-Bold";
font-size: 16pt;
--paper-input-container-underline: {
background-color: var(--paper-grey-400);
}
--paper-input-container-label: {
color: var(--paper-grey-400);
}
--paper-input-container-input: {
color: var(--paper-black);
}
--paper-input-container-focus-color: var(--paper-blue-600);
}
#container {
height: 100%;
width: 100%;
background-image: url("../../res/img/loginBack.jpg");
background-size: cover;
background-color: white;
}
#logomyCtn {
z-index: 100;
position: absolute;
margin-left: 80px;
margin-top: 150px;
}
#logomyCtn iron-image {
--iron-image-width: 16vw;
--iron-image-height: 10vw;
}
paper-card {
--paper-card-header: {
#apply(--layout-horizontal);
#apply(--layout-end);
background-color: var(--paper-blue-600);
height: 100px;
}
--paper-card-content: {
margin-right: 50px;
margin-left: 50px;
}
--paper-card-actions: {
#apply(--layout-vertical);
#apply(--layout-center);
}
--paper-card-header-text: {
color: #fff;
position: absolute;
font-family: "Roboto-Light";
bottom: 0px;
left: 0px;
}
--paper-card-header-image: {
width: 146px;
height: 32px;
margin-left: auto;
margin-right: auto;
margin-bottom: 16px;
}
}
paper-button.blue {
font-size: 12pt;
background: var(--paper-blue-600);
color: #fff;
font-family: "Roboto-Medium";
margin-top: 10px;
width: 14vw;
}
paper-button.small {
font-size: 13px;
text-transform: lowercase;
--paper-button-ink-color: transparent;
font-family: "Roboto-Light";
color: var(--paper-grey-700);
}
paper-button.pwd {
font-size: 13px;
text-transform: lowercase;
--paper-button-ink-color: transparent;
font-family: "Roboto-Light";
color: var(--paper-grey-700);
}
#logoContainer {
width: 100%;
height: 20%;
#apply(--layout-horizontal);
#apply(--layout-center);
}
iron-image {
margin-left: auto;
margin-right: auto;
}
#loginContainer {
width: 400px;
height: 60%;
margin-left: auto;
margin-right: auto;
#apply(--layout-vertical);
#apply(--layout-center-justified);
}
#versionContainer {
width: 100%;
height: 20%;
#apply(--layout-horizontal);
#apply(--layout-end);
}
#version {
margin-left: auto;
margin-right: auto;
margin-bottom: 25px;
font-family: "Roboto-Light";
font-size: 13px;
color: var(--paper-grey-700);
}
</style>
<template>
<div id="container">
<div id="logomyCtn">
<iron-image src="../../res/img/logo.png"></iron-image>
</div>
<div id="logoContainer"></div>
<div id="loginContainer">
<paper-card elevation="1" image="../../res/img/logologin.png">
<div class="card-content" on-keypress="_keyHandler">
<paper-input id="login" label="[[i18n('uid')]]"></paper-input>
<paper-input id="pwd" label="[[i18n('pwd')]]" type="password"></paper-input>
<paper-input id="server" label="[[i18n('server')]]"></paper-input>
</div>
<div class="card-actions">
<paper-button class="blue" on-click="_login">[[i18n("connect")]]</paper-button>
<paper-button class="pwd" on-click="_openPwd">[[i18n("forgotPwd")]]</paper-button>
</div>
</paper-card>
</div>
<div id="versionContainer">
<div id="version">
[[i18n("version")]]
<paper-button class="small" on-click="_openContact">[[i18n("contact")]]</paper-button>
</div>
</div>
<my-dialog id="dialog"></my-dialog>
</div>
</template>
<link rel="import" href="../../utils/behaviors/communication/loginCommunication.html">
<link rel="import" href="../../utils/behaviors/storage/localCache.html">
<link rel="import" href="../../utils/behaviors/storage/sessionCache.html">
<script>
Polymer({
is: 'my-login',
// don't paste code, only classical login code
});
</script>
</dom-module>
my-loader-synchronizer.html
<dom-module id="my-loader-synchronizer">
<style>
:host {
display: block;
background-image: url("../../res/img/loginBack.jpg");
background-size: cover;
}
#mainCtn {
height: 100%;
width: 100%;
position: absolute;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
#loading {
margin-top: 16px;
font-size: 20px;
font-family: "Roboto";
color: var(--paper-grey-700);
}
paper-progress {
width: 50%;
--paper-progress-active-color: var(--paper-blue-500);
}
#logoCtn {
z-index: 100;
position: absolute;
margin-left: 80px;
margin-top: 150px;
}
#logoCtn iron-image {
--iron-image-width: 16vw;
--iron-image-height: 10vw;
}
</style>
<template>
<div id="mainCtn">
<paper-progress class="transiting" value="{{currentResponse}}" max="{{totalData}}"></paper-progress>
<div id="loading">[[i18n("loading")]]</div>
</div>
<div id="logoCtn">
<iron-image src="../../res/img/logo.png"></iron-image>
</div>
</template>
<link rel="import" href="../../utils/behaviors/storage/ramCache.html">
<link rel="import" href="../../utils/behaviors/storage/sessionCache.html">
<link rel="import" href="../../utils/behaviors/entities/entitiesParser.html">
<link rel="import" href="../../utils/behaviors/communication/loaderSynchronizerCommunication.html">
<script>
Polymer({
is: 'my-loader-synchronizer',
created : function () {
this.i18n = i18n.getInstance();
},
properties: {
"data" : {
"type" : Array,
"value" : function () {
return [{
"entity" : "UserEntity",
"okCb" : this.usersCallback.bind(this),
"koCb" : this.usersCallbackError.bind(this)
},{
"entity" : "VehicleGroup",
"okCb" : this.vehicleGroupCallback.bind(this),
"koCb" : this.vehicleGroupCallbackError.bind(this)
},{
// multiple definitions
}]
}
}
},
behaviors : [loaderSynchronizerCommunication, entitiesParser],
ready: function ready() {
this.ramCache = ramCache.getInstance();
this.sessionCache = sessionCache.getInstance();
},
synchronizeData : function synchronizeData () {
this.totalData = this.data.length;
this.currentResponse = 0;
var i = this.data.length,
params = {
"token" : this.sessionCache.get("token")
};
while (i--) {
if (this.data[i].entity) {
var entity = this.data[i];
if (!entity.type || entity.type == "entity") {
this.prepareEntityRequest(entity.entity, params, entity.okCb, entity.koCb);
} else if (entity.type == "light") {
this.prepareLightRequest(entity.entity, params, entity.okCb, entity.koCb);
}
}
}
},
_updateRoutine : function _updateRoutine () {
var i = this.routine.length,
params = {
"token" : this.sessionCache.get("token")
};
while (i--) {
if (this.routine[i].entity) {
var entity = this.routine[i];
this.prepareEntityRequest(entity.entity, params, entity.okCb, entity.koCb);
}
}
},
_changeProgress : function _changeProgress (entity) {
this.currentResponse++;
if (this.currentResponse == this.totalData) {
this.timeoutCtrl = setTimeout(this._fireClose.bind(this), 1000);
}
},
_fireClose : function _fireClose () {
clearTimeout(this.timeoutCtrl);
// will be catch by my-application
this.fire("close-loader-synchronizer");
},
/* CALLBACK */
/*********/
/* USERS */
/*********/
usersCallback : function usersCallback (response) {
this.parseUsers(response);
this._changeProgress("UserEntity");
},
usersCallbackError : function usersCallbackError (error) {
this.fire("show-toast", {"type" : "error", "text" : "userEntityError"});
},
/*****************/
/* VEHICLES GROUP*/
/*****************/
vehicleGroupCallback : function vehicleGroupCallback (response) {
this.parseVehicleGroup(response);
this._changeProgress("VehicleGroup");
},
vehicleGroupCallbackError : function vehicleGroupCallbackError (error) {
this.fire("show-toast", {"type" : "error", "text" : "vehicleGroupEntityError"});
},
});
</script>
</dom-module>
my-header.html
<dom-module id="my-header">
<style>
:host {}
paper-toolbar {
--paper-toolbar-background: var(--paper-blue-500);
}
.first {
font-family : "Roboto-Bold";
}
.last {
font-family: "Roboto-Light";
}
</style>
<template>
<paper-toolbar>
<my-menu-button on-tap="_tapToggleButton"></my-menu-button>
<div class="title">[[i18n("product")]] [[i18n("version")]]</div>
</paper-toolbar>
</template>
<script>
Polymer({
is: 'my-header',
created : function () {
this.i18n = i18n.getInstance();
},
properties: {},
ready: function ready() {},
_tapToggleButton : function _tapToggleButton () {
this.fire("toggle-drawer");
},
});
</script>
</dom-module>
my-dashboard.html
<dom-module id="my-dashboard">
<style>
:host {
font-family : "Roboto";
}
</style>
<template>
<paper-drawer-panel id="drawerPanel" responsive-width="1300px">
<my-dashboard-left-panel drawer id="leftpanel"></my-dashboard-left-panel>
<my-dashboard-content main id="content"></my-dashboard-content>
</paper-drawer-panel>
</template>
<link rel="import" href="../../utils/behaviors/communication/childNotifications.html">
<script>
Polymer({
is: 'my-dashboard',
properties: {},
behaviors : [childNotifications, Polymer.IronResizableBehavior],
ready: function ready() {},
/************************/
/*** PUBLIC FUNCTIONS ***/
/************************/
onShow : function onShow () {
this.notifyChild("showDashboard");
},
/**
* toggle left panel
**/
toggleDrawer : function toggleDrawer () {
var responsiveWidth = parseInt(this.$.drawerPanel.responsiveWidth.replace("px", ""));
if (responsiveWidth < this.$.drawerPanel.offsetWidth) {
this.$.drawerPanel.forceNarrow = !this.$.drawerPanel.forceNarrow;
setTimeout(function () {
clearInterval(this.toto)
}.bind(this), 1000);
setInterval(function () {
this.$.content.resize();
// notify children that they need to resize
this.notifyResize();
}.bind(this), 100)
} else {
this.$.drawerPanel.togglePanel();
}
}
});
</script>
</dom-module>
I past only files which seems to be important.
So, how it works?
At first, when display my-login will be display to allow user to log in (routing is made with page.js)
There is no problem on the login screen.
When user is logged, route to /dashboard, and display the paper-header-panel and my-loader-synchronizer to sync all data and display a progress bar. When all is done, the synchronizer will be hide to make the paper-header-panel visible (screenshot are take at this moment)
When the synchronize is shown, the styles problems start.
If you need more files, don't hesitate.
thanks a lot !

Default value is not showing for input number type

I'm trying to change the code from showing a placeholder "quantity" to having a default value of 1. Users have given feedback that it would help to have a common value in place, instead of having to enter it.
Here's what I tried, it removes the placeholder, but the number field looks blank. (when I inspect element, it does show my code, just the number does not appear in the box.) I'm not very experienced in input coding, any help is appreciated. Thanks!
<div class="bo-quantity-input-section bo-col-3">
<input type="number" value="1"
ng-model="lineItem.quantity"
ng-change="quantityChanged()"
tabindex="[[1000 + 2 * index + 1]]"/>
</div>
Here's the original chunk of code:
<div class="bo-quantity-input-section bo-col-3">
<input type="number" placeholder="Quantity"
ng-model="lineItem.quantity"
ng-change="quantityChanged()"
tabindex="[[1000 + 2 * index + 1]]"/>
</div>
Here's the entire page:
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script><script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/angucomplete-alt/1.3.0/angucomplete-alt.min.js"></script>
<div ng-app="bulkOrderAppModule">
<div ng-controller="BulkOrderRootCtrl" class="bo-app"><bo-line-item ng-repeat="lineItem in lineItems" line-item="lineItem" index="$index" all-line-items="lineItems"> </bo-line-item>
<div class="bo-row">
<div class="bo-add-line-item bo-col-12"><a ng-click="addLineItem()"> Add Line Item </a></div>
</div>
<div class="bo-row">
<div class="bo-cart-controls">
<div class="bo-cart-link bo-col-6"> Go to Cart - Total: <span ng-bind-html="cart['total_price'] | shopifyMoneyFormat"></span>  |  [[cart['item_count'] ]] Items </div>
<div class="bo-clear-cart bo-col-3"><a ng-click="clearCart()"> Clear Cart </a></div>
<div class="bo-update-cart bo-col-3"><button class="btn bo-update-cart-btn" ng-disabled="!hasChanges" ng-click="updateCart()"> Update Cart </button></div>
</div>
</div>
</div>
<script type="text/ng-template" id="line-item-template">// <![CDATA[
<div class="bo-line-item">
<div class="bo-row">
<div class="bo-variant-input-section bo-col-8">
<angucomplete-alt ng-if="!lineItem.searchResult"
placeholder="Search for products by name or SKU"
pause="400"
selected-object="selectResult"
remote-url="/search?type=product&view=bulk-order-json&q="
remote-url-data-field="results"
title-field="product_title,variant_title"
image-field="thumbnail_url"
input-class="bo-variant-input"
bo-configure-angucomplete bo-tabindex="[[1000 + 2 * index]]">
</angucomplete-alt>
<div ng-if="lineItem.searchResult">
<div class="bo-col-2 bo-img-container">
<img class="bo-img" ng-src="[[lineItem.searchResult['thumbnail_url'] ]]"/>
</div>
<div class="bo-col-10">
<div class="bo-line-item-details">
[[lineItem.searchResult['product_title'] ]]
<span ng-if="lineItem.searchResult['variant_title']">
-
[[lineItem.searchResult['variant_title'] ]]
</span>
<span ng-if="lineItem.searchResult['sku']">
-
[[lineItem.searchResult['sku'] ]]
</span>
</div>
<div class="bo-line-item-price" ng-if="lineItem.searchResult['price']">
Unit price: <span ng-bind-html="lineItem.searchResult['price'] | shopifyMoneyFormat"></span>
</div>
<div ng-if="numVariants() > 1 && !lineItem.expanded">
<a href="javascript:void(0)" ng-click="expandAllVariants()">
Expand all [[numVariants()]] variants
</a>
</div>
</div>
</div>
</div>
<div class="bo-quantity-input-section bo-col-3">
<input type="number" placeholder="Quantity"
ng-model="lineItem.quantity"
ng-change="quantityChanged()"
tabindex="[[1000 + 2 * index + 1]]"/>
</div>
<div class="bo-remove-section bo-col-1">
<a href="javascript:void(0)" ng-click="deleteLineItem()">
<div class="bo-svg-container">
<svg viewBox="0 0 49 49" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<path d="M24.486,-3.55271368e-15 C10.984,-3.55271368e-15 -3.55271368e-15,10.985 -3.55271368e-15,24.486 C-3.55271368e-15,37.988 10.984,48.972 24.486,48.972 C37.988,48.972 48.972,37.988 48.972,24.486 C48.972,10.984 37.988,-3.55271368e-15 24.486,-3.55271368e-15 L24.486,-3.55271368e-15 Z M24.486,45.972 C12.638,45.972 3,36.331 3,24.485 C3,12.637 12.639,3 24.486,3 C36.334,3 45.972,12.638 45.972,24.486 C45.972,36.334 36.334,45.972 24.486,45.972 L24.486,45.972 Z M32.007,16.965 C31.42,16.379 30.471,16.379 29.885,16.965 L24.486,22.365 L19.087,16.966 C18.501,16.38 17.552,16.38 16.966,16.966 C16.381,17.551 16.38,18.502 16.966,19.088 L22.365,24.486 L16.965,29.886 C16.38,30.471 16.379,31.421 16.965,32.007 C17.55,32.593 18.501,32.592 19.086,32.007 L24.486,26.607 L29.884,32.006 C30.47,32.591 31.42,32.591 32.005,32.006 C32.592,31.419 32.591,30.47 32.005,29.884 L26.607,24.486 L32.007,19.087 C32.593,18.501 32.592,17.551 32.007,16.965 L32.007,16.965 Z" id="Shape" fill="#404040" sketch:type="MSShapeGroup"></path>
</g>
</svg>
</div>
</a>
</div>
</div>
<div class="bo-row" ng-if="showLineItemAlreadyExistsMsg">
<div class="bo-col-12 bo-line-item-already-exists-msg">
A line item already exists for that product
</div>
</div>
</div>
// ]]></script><script>// <![CDATA[
var template = document.getElementById('line-item-template');
template.innerHTML = template.innerHTML.replace('// <![CDATA[', '').replace('// ]]>', '');
// ]]></script></div>
<style><!--
.bo-app {
padding: 20px 0px 10px 0px;
border: 1px solid #EEEEEE;
}
.bo-variant-input-section {
display: inline-block;
}
.bo-quantity-input-section {
display: inline-block;
}
.bo-remove-section {
display: inline-block;
height: 50px;
line-height: 50px;
text-align: center;
}
.bo-svg-container {
padding: 5px 0px;
}
.bo-remove-section svg {
height: 25px;
width: 25px;
}
.bo-variant-input {
width: 100%;
}
.bo-line-item {
margin-bottom: 20px;
}
.bo-line-item input {
height: 50px !important;
width: 100% !important;
padding-top: 0px !important;
padding-bottom: 0px !important;
}
.bo-img {
margin-left: 0px !important;
max-height: 50px !important;
width: auto !important;
}
.bo-img-container {
max-height: 50px !important;
}
.bo-line-item-details {
font-size: 1.05rem;
}
.angucomplete-searching {
padding: 0px 5px;
font-size: 1.05rem;
}
.angucomplete-holder{
position: relative;
}
.angucomplete-dropdown {
margin-top: 0px;
padding: 20px 10px;
background-color: #FCFCFC;
border: 1px solid #DDDDDD;
max-height: 360px;
overflow: scroll;
position: absolute;
z-index: 999;
width: 100%;
}
.angucomplete-row {
min-height: 50px;
margin-bottom: 20px;
cursor: pointer;
}
.angucomplete-image-holder {
width: calc(16.66666667% - 20px);
float: left;
padding: 0px 5px;
margin: 0px !important;
}
.angucomplete-image {
max-height: 50px !important;
width: auto !important;
}
.angucomplete-title {
width: calc(83.33333333% - 20px);
float: left;
font-size: 1.05rem;
padding: 0px 5px;
}
.bo-add-line-item {
position: relative;
font-size: 1.05rem;
margin-top: 10px;
}
.bo-cart-controls {
margin-top: 20px;
font-size: 1.05rem;
}
.bo-clear-cart,
.bo-update-cart {
text-align: right;
margin-bottom: 10px;
}
.bo-update-cart-btn {
float: right;
max-width: 80%;
position: relative;
bottom: 5px;
}
.bo-line-item-already-exists-msg {
color: red;
}
.bo-row {
padding: 0px 10px;
min-height: 50px;
}
.bo-col-1 {
width: calc(8.33333333% - 20px);
float: left;
}
.bo-col-2 {
width: calc(16.6666667% - 20px);
float: left;
}
.bo-col-3 {
width: calc(25% - 20px);
float: left;
}
.bo-col-4 {
width: calc(33.33333333% - 20px);
float: left;
}
.bo-col-5 {
width: calc(41.66666667% - 20px);
float: left;
}
.bo-col-6 {
width: calc(50% - 20px);
float: left;
}
.bo-col-7 {
width: calc(58.33333333% - 20px);
float: left;
}
.bo-col-8 {
width: calc(66.66666667% - 20px);
float: left;
}
.bo-col-9 {
width: calc(75% - 20px);
float: left;
}
.bo-col-10 {
width: calc(83.33333333% - 20px);
float: left;
}
.bo-col-11 {
width: calc(91.66666667% - 20px);
float: left;
}
.bo-col-12 {
width: calc(100% - 20px);
float: left;
}
.bo-col-1, .bo-col-2, .bo-col-3, .bo-col-4, .bo-col-5, .bo-col-6,
.bo-col-7, .bo-col-8, .bo-col-9, .bo-col-10, .bo-col-11, .bo-col-12 {
margin: 0px 10px;
}
--></style><script>// <![CDATA[
(function() {
function BulkOrderRootCtrl($scope, $http, $timeout) {
$scope.lineItems = [];
$scope.cart = null;
$scope.hasChanges = false;
$http.get('/cart.js').success(function(response) {
$scope.cart = response;
});
$scope.addLineItem = function(opt_initial) {
$scope.lineItems.push({
searchResult: null,
expanded: false,
quantity: null
});
if (!opt_initial) {
$scope.hasChanges = true;
}
};
// Initialize the first empty line item in a timeout.
// Certain themes look for number inputs at page load time
// and replace them with custom widgets.
$timeout(function() {
$scope.addLineItem(true);
});
$scope.updateCart = function() {
$http.post('/cart/update.js', {
'updates': _.reduce($scope.lineItems, function(obj, lineItem) {
if (lineItem.searchResult && _.isNumber(lineItem.quantity)) {
obj[lineItem.searchResult['variant_id']] = lineItem.quantity;
}
return obj;
}, {})
})
.success(function(response) {
$scope.cart = response;
$scope.hasChanges = false;
$scope.lineItems = _.filter($scope.lineItems, function(lineItem) {
return lineItem.quantity > 0;
});
})
.error(function(response) {
// Handle out of stock here
console.log(response);
});
};
$scope.clearCart = function() {
$http.post('/cart/clear.js')
.success(function(response) {
$scope.cart = response;
$scope.lineItems = [];
$scope.hasChanges = false;
});
};
$scope.$on('quantity-changed', function() {
$scope.hasChanges = true;
});
$scope.$on('delete-line-item', function(event, lineItem) {
var idx = $scope.lineItems.indexOf(lineItem);
if (idx != -1) {
$scope.lineItems.splice(idx, 1);
}
});
$scope.$on('expand-all-variants', function(event, lineItem) {
var idx = $scope.lineItems.indexOf(lineItem);
if (idx != -1) {
var args = [idx, 1];
angular.forEach(lineItem.searchResult['product']['variants'], function(variant) {
var imageUrl = '';
if (variant['featured_image'] && variant['featured_image']['src']) {
imageUrl = variant['featured_image']['src']
} else if (lineItem.searchResult['product']['featured_image']) {
imageUrl = lineItem.searchResult['product']['featured_image'];
}
args.push({
quantity: lineItem.searchResult['variant_id'] == variant['id'] ? lineItem.quantity : null,
expanded: true,
searchResult: {
'product_title': lineItem.searchResult['product_title'],
'variant_title': variant['title'],
'variant_id': variant['id'],
'sku': variant['sku'],
'price': variant['price'],
'url': variant['url'],
'product': lineItem.searchResult['product'],
'thumbnail_url': shopifyImageUrl(imageUrl, 'thumb')
}
});
});
Array.prototype.splice.apply($scope.lineItems, args);
}
});
}
function boLineItem() {
return {
scope: {
lineItem: '=',
index: '=',
allLineItems: '='
},
templateUrl: 'line-item-template',
controller: function($scope) {
$scope.showLineItemAlreadyExistsMsg = false;
$scope.selectResult = function(result) {
$scope.showLineItemAlreadyExistsMsg = false;
if ($scope.variantLineItemAlreadyExists(result.originalObject['variant_id'])) {
$scope.showLineItemAlreadyExistsMsg = true;
} else {
$scope.lineItem.searchResult = result.originalObject;
}
};
$scope.variantLineItemAlreadyExists = function(variantId) {
var exists = false;
angular.forEach($scope.allLineItems, function(lineItem) {
if (lineItem !== $scope.lineItem && lineItem.searchResult['variant_id'] == variantId) {
exists = true;
}
});
return exists;
};
$scope.quantityChanged = function() {
$scope.$emit('quantity-changed');
};
$scope.deleteLineItem = function() {
if (_.isNumber($scope.lineItem.quantity)) {
$scope.lineItem.quantity = 0;
$scope.quantityChanged();
} else {
$scope.$emit('delete-line-item', $scope.lineItem);
}
};
$scope.numVariants = function() {
return $scope.lineItem.searchResult['product']['variants'].length;
};
$scope.expandAllVariants = function() {
$scope.$emit('expand-all-variants', $scope.lineItem);
};
}
};
}
function boConfigureAngucomplete($timeout) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var input = element.find('input');
input.attr('tabindex', attrs.boTabindex);
$timeout(function() {
input.focus();
});
}
};
}
function shopifyImageUrl(url, imageType) {
if (url.indexOf('_' + imageType + '.') != -1) {
return url;
}
var dotIdx = url.lastIndexOf('.');
return [url.slice(0, dotIdx), '_', imageType, url.slice(dotIdx, url.length)].join('');
}
function shopifyMoneyFormat($shopifyMoneyFormatString, $sce) {
return function(cents) {
return $sce.trustAsHtml(Shopify.formatMoney(cents, $shopifyMoneyFormatString));
};
}
function interpolator($interpolateProvider) {
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
}
// Polyfill for themes that don't include these:
function polyfillShopifyBuiltins() {
if (!window['Shopify']) {
window['Shopify'] = {};
}
if (!Shopify.formatMoney) {
Shopify.formatMoney = function(cents, format) {
if (typeof cents == 'string') cents = cents.replace('.','');
var value = '';
var patt = /\{\{\s*(\w+)\s*\}\}/;
var formatString = (format || this.money_format);
function addCommas(moneyString) {
return moneyString.replace(/(\d+)(\d{3}[\.,]?)/,'$1,$2');
}
switch(formatString.match(patt)[1]) {
case 'amount':
value = addCommas(floatToString(cents/100.0, 2));
break;
case 'amount_no_decimals':
value = addCommas(floatToString(cents/100.0, 0));
break;
case 'amount_with_comma_separator':
value = floatToString(cents/100.0, 2).replace(/\./, ',');
break;
case 'amount_no_decimals_with_comma_separator':
value = addCommas(floatToString(cents/100.0, 0)).replace(/\./, ',');
break;
}
return formatString.replace(patt, value);
};
if (!window['floatToString']) {
window['floatToString'] = function(numeric, decimals) {
var amount = numeric.toFixed(decimals).toString();
if(amount.match(/^\.\d+/)) {return "0"+amount; }
else { return amount; }
}
}
}
}
polyfillShopifyBuiltins();
angular.module('bulkOrderAppModule', ['angucomplete-alt'], interpolator)
.controller('BulkOrderRootCtrl', BulkOrderRootCtrl)
.directive('boLineItem', boLineItem)
.directive('boConfigureAngucomplete', boConfigureAngucomplete)
.filter('shopifyMoneyFormat', shopifyMoneyFormat)
.value('$shopifyMoneyFormatString', BO_MONEY_FORMAT);
})();
// ]]></script>