Anchor tags to jump halfway down a page in NetSuite (SuiteCommerce Advanced) - suitecommerce

Does NetSuite SuiteCommerce not support anchor tags? I want to include some links at the top of my page that let the viewer jump down to that category (on the same page). I know I have my code set up properly, but all that happens is that it tries to open a new page with no content on it:
https://www.prospectfastener.com/product-interchange

Using Anchor Tags with NetSuite SuiteCommerce
Bundled together a few answers from other Backbone related questions. This can be used in custom extensions or it can be placed in to SMT CMS HTML Content as an inline script.
Tested with SC 2022.1
// This breaks the function out of the try/catch for use on page
var scrollToAnchorFn = null;
// SB env keeps doubling loading inline scripts for testing
// This tests/sets its own property on the window to test against for that
if (typeof window.anchorSet === 'undefined') {
try {
window.anchorSet = true;
// Call scrollToAnchor with anchor ID whenever you want to jump between points on a page
const scrollToAnchor = (anchorId) => {
try {
var aTag = $(anchorId);
$('html,body').animate({
scrollTop: aTag.offset().top
},
'slow',
);
} catch (err) {
console.error(err);
}
};
const initAnchorScroll = () => {
// Test if jQuery is loaded yet, if not set timeout and repeat fn
if (window.jQuery) {
// Check if document is already loaded and ready, run code now if dom is ready
if (document.readyState === 'complete')
return scrollToAnchor(PageHash);
// Before images or styling is finished loading, move to part of dom requested which is ready
return $(document).ready(() => {
scrollToAnchor(PageHash);
});
}
setTimeout(function() {
initAnchorScroll();
}, 50);
};
// Get our page hash
// const PageHash = window.location.hash;
// Snippet demo hash
const PageHash = '#testAnchor';
// Initialize on page load if hash detected in url
// Since hashes return the #,
// we want a length greater than 1 to ensure there is an id
if (PageHash.length > 1) initAnchorScroll();
scrollToAnchorFn = scrollToAnchor;
} catch (err) {
console.error(err);
}
}
.fillerDiv {
height: 100vh;
width: 1000vw;
background-color: #fedd00;
}
#testAnchor {
background-color: #4285f4;
color: #ffffff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.2.3/jquery.min.js"></script>
<div class="container">
<div class="fillerDiv"></div>
<div id="testAnchor">this is test content down the page with anchor ID.
Go to Next Anchor</div>
<div class="fillerDiv "></div>
<div id="testAnchor2">Second anchor tag</div>
<div class="fillerDiv "></div>
</div>
Old Answer
I'm going to assume not, but I'd be happy to be wrong.
Note: Anchor Tags are not supported by Content Delivery.
NetSuite Applications Suite -- Understanding Content Delivery

Related

How to close component via changing the state [duplicate]

I'm currently using jQuery to make a div clickable and in this div I also have anchors. The problem I'm running into is that when I click on an anchor both click events are firing (for the div and the anchor). How do I prevent the div's onclick event from firing when an anchor is clicked?
Here's the broken code:
JavaScript
var url = $("#clickable a").attr("href");
$("#clickable").click(function() {
window.location = url;
return true;
})
HTML
<div id="clickable">
<!-- Other content. -->
I don't want #clickable to handle this click event.
</div>
Events bubble to the highest point in the DOM at which a click event has been attached. So in your example, even if you didn't have any other explicitly clickable elements in the div, every child element of the div would bubble their click event up the DOM to until the DIV's click event handler catches it.
There are two solutions to this is to check to see who actually originated the event. jQuery passes an eventargs object along with the event:
$("#clickable").click(function(e) {
var senderElement = e.target;
// Check if sender is the <div> element e.g.
// if($(e.target).is("div")) {
window.location = url;
return true;
});
You can also attach a click event handler to your links which tell them to stop event bubbling after their own handler executes:
$("#clickable a").click(function(e) {
// Do something
e.stopPropagation();
});
Use stopPropagation method, see an example:
$("#clickable a").click(function(e) {
e.stopPropagation();
});
As said by jQuery Docs:
stopPropagation method prevents the event from bubbling up the DOM
tree, preventing any parent handlers from being notified of the event.
Keep in mind that it does not prevent others listeners to handle this event(ex. more than one click handler for a button), if it is not the desired effect, you must use stopImmediatePropagation instead.
Here my solution for everyone out there looking for a non-jQuery code (pure javascript)
document.getElementById("clickable").addEventListener("click", function(e) {
e = window.event || e;
if(this === e.target) {
// put your code here
}
});
Your code wont be executed if clicked on parent's children
If you do not intend to interact with the inner element/s in any case, then a CSS solution might be useful for you.
Just set the inner element/s to pointer-events: none
in your case:
.clickable > a {
pointer-events: none;
}
or to target all inner elements generally:
.clickable * {
pointer-events: none;
}
This easy hack saved me a lot of time while developing with ReactJS
Browser support could be found here: http://caniuse.com/#feat=pointer-events
Inline Alternative:
<div>
<!-- Other content. -->
<a onclick='event.stopPropagation();' href="http://foo.example">I don't want #clickable to handle this click event.</a>
</div>
You can also try this
$("#clickable").click(function(event) {
var senderElementName = event.target.tagName.toLowerCase();
if(senderElementName === 'div') {
// Do something here
} else {
// Do something with <a> tag
}
});
Writing if anyone needs (worked for me):
event.stopImmediatePropagation()
From this solution.
Using return false; or e.stopPropogation(); will not allow further code to execute. It will stop flow at this point itself.
If you have multiple elements in the clickable div, you should do this:
$('#clickable *').click(function(e){ e.stopPropagation(); });
I compare to ev.currentTarget when this is not available (React, etc).
$("#clickable").click(function(e) {
if (e.target === e.currentTarget) {
window.location = url;
return true;
}
})
Here's an example using Angular 2+
For example, if you wanted to close a Modal Component if the user clicks outside of it:
// Close the modal if the document is clicked.
#HostListener('document:click', ['$event'])
public onDocumentClick(event: MouseEvent): void {
this.closeModal();
}
// Don't close the modal if the modal itself is clicked.
#HostListener('click', ['$event'])
public onClick(event: MouseEvent): void {
event.stopPropagation();
}
If it is in inline context, in HTML try this:
onclick="functionCall();event.stopPropagation();
e.stopPropagation() is a correct solution, but in case you don't want to attach any event handler to your inner anchor, you can simply attach this handler to your outer div:
e => { e.target === e.currentTarget && window.location = URL; }
var inner = document.querySelector("#inner");
var outer = document.querySelector("#outer");
inner.addEventListener('click',innerFunction);
outer.addEventListener('click',outerFunction);
function innerFunction(event){
event.stopPropagation();
console.log("Inner Functiuon");
}
function outerFunction(event){
console.log("Outer Functiuon");
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Pramod Kharade-Event with Outer and Inner Progration</title>
</head>
<body>
<div id="outer" style="width:100px;height:100px;background-color:green;">
<div id="inner" style="width:35px;height:35px;background-color:yellow;"></div>
</div>
</body>
</html>
You need to stop the event from reaching (bubbling to) the parent (the div).
See the part about bubbling here, and jQuery-specific API info here.
To specify some sub element as unclickable write the css hierarchy as in the example below.
In this example I stop propagation to any elements (*) inside td inside tr inside a table with the class ".subtable"
$(document).ready(function()
{
$(".subtable tr td *").click(function (event)
{
event.stopPropagation();
});
});
You can check whether the target is not your div-element and then issue another click event on the parent after which you will "return" from the handle.
$('clickable').click(function (event) {
let div = $(event.target);
if (! div.is('div')) {
div.parent().click();
return;
}
// Then Implement your logic here
}
Here is a non jQuery solution that worked for me.
<div style="background:cyan; width:100px; height:100px;" onclick="if (event.srcElement==this) {console.log('outer');}">
<a style="background:red" onclick="console.log('inner');">Click me</a>
</div>
for those that are not using jQuery
document.querySelector('.clickable').addEventListener('click', (e) =>{
if(!e.target.classList.contains('clickable')) return
// place code here
})
In case someone had this issue using React, this is how I solved it.
scss:
#loginBackdrop {
position: absolute;
width: 100% !important;
height: 100% !important;
top:0px;
left:0px;
z-index: 9; }
#loginFrame {
width: $iFrameWidth;
height: $iFrameHeight;
background-color: $mainColor;
position: fixed;
z-index: 10;
top: 50%;
left: 50%;
margin-top: calc(-1 * #{$iFrameHeight} / 2);
margin-left: calc(-1 * #{$iFrameWidth} / 2);
border: solid 1px grey;
border-radius: 20px;
box-shadow: 0px 0px 90px #545454; }
Component's render():
render() {
...
return (
<div id='loginBackdrop' onClick={this.props.closeLogin}>
<div id='loginFrame' onClick={(e)=>{e.preventDefault();e.stopPropagation()}}>
... [modal content] ...
</div>
</div>
)
}
By a adding an onClick function for the child modal (content div) mouse click events are prevented to reach the 'closeLogin' function of the parent element.
This did the trick for me and I was able to create a modal effect with 2 simple divs.
If a child element is clicked, then the event bubbles up to the parent and event.target !== event.currentTarget.
So in your function, you can check this and return early, i.e.:
var url = $("#clickable a").attr("href");
$("#clickable").click(function(event) {
if ( event.target !== event.currentTarget ){
// user clicked on a child and we ignore that
return;
}
window.location = url;
return true;
})
This is what you are looking for
mousedown event. this works on every DOM elements to prevent javascript focus handler like this:
$('.no-focus').mousedown(function (e) {
e.prevenDefault()
// do stuff
}
in vue.js framework, you can use modifier like this:
<span #mousedown.prevent> no focus </span>
Note that using on the input will prevent text selection handler
add a as follows:
....
or return false; from click handler for #clickable like:
$("#clickable").click(function() {
var url = $("#clickable a").attr("href");
window.location = url;
return false;
});
All solution are complicated and of jscript. Here is the simplest version:
var IsChildWindow=false;
function ParentClick()
{
if(IsChildWindow==true)
{
IsChildWindow==false;
return;
}
//do ur work here
}
function ChildClick()
{
IsChildWindow=true;
//Do ur work here
}
<a onclick="return false;" href="http://foo.example">I want to ignore my parent's onclick event.</a>

Insert raw html code to quill

Is there option to insert raw html code to quill?
quill.insertText();
quill.clipboard.dangerouslyPasteHTML()
both are parsed by matcher but I need to paste exactly formatted html code for an email footer.
If the footer content is meant to be static and un-editable, you can do this by extending the BlockEmbed blot then adding a button for your new format in the toolbar. There are 2 different ways to handle what HTML get's entered into the new format.
1. Let the user enter the HTML to embed:
// Import the BlockEmbed blot.
var BlockEmbed = Quill.import('blots/block/embed');
// Create a new format based off the BlockEmbed.
class Footer extends BlockEmbed {
// Handle the creation of the new Footer format.
// The value will be the HTML that is embedded.
// By default, the toolbar will show a prompt window to get the value.
static create(value) {
// Create the node using the BlockEmbed's create method.
var node = super.create(value);
// Set the srcdoc attribute to equal the value which will be your html.
node.setAttribute('srcdoc', value);
// Add a few other iframe fixes.
node.setAttribute('frameborder', '0');
node.setAttribute('allowfullscreen', true);
node.setAttribute('width', '100%');
return node;
}
// return the srcdoc attribute to represent the Footer's value in quill.
static value(node) {
return node.getAttribute('srcdoc');
}
}
// Give our new Footer format a name to use in the toolbar.
Footer.blotName = 'footer';
// Give it a class name to edit the css.
Footer.className = 'ql-footer';
// Give it a tagName of iframe to tell quill what kind of element to create.
Footer.tagName = 'iframe';
// Lastly, register the new Footer format so we can use it in our editor.
Quill.register(Footer, true);
var quill = new Quill('#editor-container', {
modules: {
toolbar: {
container: ['footer'] // Toolbar with just our footer tool (of course you can add all you want).
}
},
theme: 'snow'
});
.ql-toolbar .ql-footer:before {
content: 'footer';
}
.ql-editor .ql-footer {
background: #f7f7f7;
}
<link href="//cdn.quilljs.com/1.3.4/quill.core.css" rel="stylesheet"/>
<link href="//cdn.quilljs.com/1.0.0/quill.snow.css" rel="stylesheet"/>
<div id="editor-container">
<h1>Test Content</h1>
<p>Enter a footer</p>
</div>
<script src="//cdn.quilljs.com/1.3.4/quill.min.js"></script>
2. Use specific HTML
// Import the BlockEmbed blot.
var BlockEmbed = Quill.import('blots/block/embed');
// Create a new format based off the BlockEmbed.
class Footer extends BlockEmbed {
// Handle the creation of the new Footer format.
// The value will be the HTML that is embedded.
// This time the value is passed from our custom handler.
static create(value) {
// Create the node using the BlockEmbed's create method.
var node = super.create(value);
// Set the srcdoc attribute to equal the value which will be your html.
node.setAttribute('srcdoc', value);
// Add a few other iframe fixes.
node.setAttribute('frameborder', '0');
node.setAttribute('allowfullscreen', true);
node.setAttribute('width', '100%');
return node;
}
// return the srcdoc attribute to represent the Footer's value in quill.
static value(node) {
return node.getAttribute('srcdoc');
}
}
// Give our new Footer format a name to use in the toolbar.
Footer.blotName = 'footer';
// Give it a class name to edit the css.
Footer.className = 'ql-footer';
// Give it a tagName of iframe to tell quill what kind of element to create.
Footer.tagName = 'iframe';
// Register the new Footer format so we can use it in our editor.
Quill.register(Footer, true);
// Specify the HTML that will be embedded.
var footerHTML = '<h1>Footer</h1>'
+ '<p>This is our new footer</p>';
// Create the footer handler.
var footerHandler = function() {
// Get the cursor location to know where footer will be added.
var index = this.quill.getSelection(true).index;
// Insert the footer with the footerHTML.
this.quill.insertEmbed(index, 'footer', footerHTML);
};
// Import the Toolbar module so we can add a custom handler to our footer button.
var Toolbar = Quill.import('modules/toolbar');
// Add our custom footer handler to the footer button.
Toolbar.DEFAULTS.handlers['footer'] = footerHandler;
var quill = new Quill('#editor-container', {
modules: {
toolbar: {
container: ['footer'] // Toolbar with just our footer tool (of course you can add all you want).
}
},
theme: 'snow'
});
.ql-toolbar .ql-footer:before {
content: 'footer';
}
.ql-editor .ql-footer {
background: #f7f7f7;
}
<link href="//cdn.quilljs.com/1.3.4/quill.core.css" rel="stylesheet"/>
<link href="//cdn.quilljs.com/1.0.0/quill.snow.css" rel="stylesheet"/>
<div id="editor-container">
<h1>Test Content</h1>
<p>Enter a footer</p>
</div>
<script src="//cdn.quilljs.com/1.3.4/quill.min.js"></script>

How to trigger animations in a React app based on the scroll position

Let's say I need to add an element to the navbar when the user have scrolled past the header of the site. How can I do something like this in React without using jQuery?
You can do some thing like this: (this function was copied from my own react-sticky-dynamic-header that I created before: https://github.com/thinhvo0108/react-sticky-dynamic-header )
componentDidMount() {
var h1 = parseInt(this.refs.header.offsetHeight);
window.addEventListener('scroll', this._calcScroll.bind(this, h1));
}
componentWillUnmount() {
window.removeEventListener('scroll', this._calcScroll)
}
_calcScroll(h1) {
var _window = window;
var heightDiff = parseInt(h1);
var scrollPos = _window.scrollY;
if (scrollPos > heightDiff) {
// here this means user has scrolled past your header,
// you may rerender by setting State or do whatever
this.setState({
//stateKey: stateValue,
});
} else {
// here the user has scrolled back to header's territory,
// it's optional here for you to remove the element on navbar as stated in the question or not
this.setState({
//stateKey: stateValue,
});
}
}
render() {
return (
<div ref="header">YOUR HEADER HERE</div>
);
}
For a smooth animation when your element added or removed from the navbar, you can just add this into the element's CSS style:
#your-element{
transition: opacity 0.3s ease-in;
}
You can try to install my library to see if it can extend your needs:
https://www.npmjs.com/package/react-sticky-dynamic-header
Feel free to post here some errors if any, thanks

How can I get ng-click to function with ng-repeat and ng-bind-html?

I am trying to get a ng-click directive to function within an ng-repeat and ng-bind-html. The ng-click code is added to a string of html from data pulled from the server (hence the ng-bind-html). The setup has a controller, a base template that is put onto the page with Drupal, and a partial that is loaded via the template from Drupal.
The controller looks like this at the moment:
var guideListController = angular.module('app')
.controller('guideListController', [
'$scope',
'$sce',
'$compile',
'ViewService',
'ToolKit',
function($scope, $sce, $compile, ViewService, ToolKit) {
// Storage for which rows need more/less links
this.rowIndex = [];
this.showFull = false;
this.showFullClick = function() {
alert('Showfull');
};
this.trustIntro = function(code) {
return $sce.trustAsHtml(code);
};
// Fetch the guide list view from services
var data = ViewService.get({view_endpoint:'guide-service', view_path: 'guide-list'}, function(data) {
//console.log(data);
// Update/process results
for (var row in data.results) {
// Create short intro w/ truncate.js
data.results[row].Intro_short = $sce.trustAsHtml($scope.guideList.getShortIntro(data.results[row], row));
//data.results[row].Intro_short = $scope.guideList.getShortIntro(data.results[row], row);
// Update intro
data.results[row].Introduction = $sce.trustAsHtml($scope.guideList.updateIntro(data.results[row], row));
//data.results[row].Introduction = $scope.guideList.updateIntro(data.results[row], row);
}
$scope.guideList.guides = data.results;
});
// Add a read less anchor tag at the end of the main intro
this.updateIntro = function(row, row_index) {
var intro = row['Introduction'].trim();
if ($scope.guideList.rowIndex[row_index]) { // only apply Less link if needed
var index = intro.length - 1;
var tag = [];
if (intro.charAt(index) === '>') { // we have a tag at the end
index--;
do {
tag.push(intro.charAt(index));
index--;
} while (intro.charAt(index) != '/'); // the closing tag
index--; // we move the index one more for the "<"
tag.reverse(); // Reverse
tag = tag.join('');
}
var inserts = ['div', 'p']; // we insert the Less link here.
if (jQuery.inArray(tag, inserts) >= 0) { // insert into the tag
intro = intro.substr(0, index) + ' <a class="less" ng-click="$parent.guideList.showFull = false">Less</a>' + intro.substr(index);
}
else { // insert at the end of the html
intro = intro + '<a class="less" ng-click="this.showFull = false">Less</a>';
}
}
return intro;
};
// Truncate the long intro into a shorter length blurb
this.getShortIntro = function(row, row_index) {
// Truncate if necc.
var short_intro = jQuery.truncate(row['Introduction'], {
length: 250,
words: true,
ellipsis: '\u2026 <a class="more moreish" attr-ng-click="guideList.showFullClick()">Read on</a>'
});
var more = jQuery('.more', short_intro); // select more link
if (more.length) { // do we have a more link
$scope.guideList.rowIndex[row_index] = true;
}
else { // no more link
$scope.guideList.rowIndex[row_index] = false;
}
$compile(short_intro)($scope);
return short_intro;
};
}]);
As you can see in the ViewService.get() call, data is fetched and then processed. The processing simply involves putting a link at the end of the "Intro" field that is intended to be clickable.
For a while I was having a tough time to even get the ng-click directive to even show (it was being filtered out w/out $sce.trustAsHtml). Now it is there but clicking it has no effect.
The main template (from Drupal) currently looks like:
<div class="guide-listing" ng-controller="guideListController as guideList">
<a ng-click="guideList.showFullClick()">Click me</a>
<div class="guide-teaser"
ng-repeat="guide in guideList.guides"
ng-include src="'/sites/all/themes/ngTheme/ngApp/partials/guide_teaser.html'">
<!-- See guide_teaser.html partial for guide teasers -->
</div>
</div>
The ng-click as placed in the Drupal template above works as expected.
And for the partial that is used in the ng-repeat, it looks like so:
<div ng-controller="guideListController as guideList">
<h2 class="guide-teaser-title">{{guide.node_title}}</h2>
<div class="guide-teaser-intro" ng-bind-html="guide.Introduction" ng-show="guide.showFull">
{{guide.Introduction}}
</div>
<div class="guide-teaser-intro-short" ng-bind-html="guide.Intro_short" ng-show="!guide.showFull">
{{guide.Intro_short}}
</div>
</div>
So far I have only been working on getting the ng-click to work on the short_intro and have had no success so far. Any ideas as to what I am doing wrong would be greatly appreciated!
Ok, So I did get some traction! I used the ngHtmlCompile (http://ngmodules.org/modules/ng-html-compile) directive that was created by https://github.com/francisbouvier (thanks!)
The issue was that the new (dynamic) html wasn't being compiled.
At first it didn't work. I had two issues that prevented it from firing:
A: I stopped using $sce.trustAsHtml. Using this in conjunction with the directive caused the content to disappear!
B: The other issue was one of scope. After I changed the directive such that transclude was set to false it worked just fine!

Back button using Extjs 3.4 History with ie8 & ie9 standards document mode

I am having a problem with Ext.History utility (version 3.4.0) working properly in IE8+. It works in Quirks mode, but not with the document mode in IE8 Standards mode (IE8) or IE9 Standards mode (IE9). Quirks mode isn't working for us because it is not rendering our CSS properly.
I have stripped everything out of the app except the history utility and now have two files (besides the extjs files):
index.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<script type="text/javascript" src="ext-base.js"></script>
<script type="text/javascript" src="ext-all.js"></script>
<script type="text/javascript" src="myapp.js"></script>
</head>
<body>
<div>
<div align="center">
<table width="97%" border="0" cellpadding="0" cellspacing="0" >
<tr>
<td>
Link1 |
Link2 |
Link3 |
Link4 |
link5
</td>
</tr>
</table>
</div>
</div>
<!-- Fields required for history management -->
<form id="history-form" class="x-hidden">
<input type="hidden" id="x-history-field"/>
<iframe id="x-history-frame"></iframe>
</form>
</body>
</html>
myapp.js:
Ext.ns('MyApp');
Ext.onReady(function()
{
Ext.History.init();
Ext.History.on('change', function(token){}, this);
});
When I load the app in a web server, go to index.html, and click link1, the address bar shows #link1. I then click link2 and the address bar shows #link2. I then click link3 and the address bar shows #link3.
Using the back button in IE with IE7 Emulation, in Chrome or in Firefox, the address bar will go from #link3 to #link2. When I hit the back button a second time, the address bar goes from #link2 to #link1. This is the behavior I expect.
However, using IE8 or IE 9 in the appropriate document standards mode, when I click the back button a second time, the address bar goes from #link2 back to #link3. Further clicks of the back button will just toggle the user between #link2 and #link3. This behavior is unexpected and is causing our application not to work properly.
Note that this is the way that the Sencha example works for 3.4.0:
Sencha 3.4 Sample
(the page renders in Quirks mode, but if you change it to IE8 Standards or IE9 Standards, it doesn't work).
It does work seem to work properly in 4.1:
(only let me posts 2 links, but you can probably find it...)
I don't have access to Ext 3.4.1, but this issue isn't listed as in the bug fixes. I have seen one thread (here) that suggests that changing the doctype would work, but that doesn't seem to be the case (I have tried all the doctypes...).
Note that many parts of our app are using the History utility for navigation, so removing it is not an acceptable solution.
Can anyone offer any suggestions on how I can get this to work?
This was actually pretty straightforward. I downloaded Ext 4.1 and looked at what they were doing with the Ext.util.History class. They define a variable for oldIEMode and use that for all the conditionals where in 3.4 they are using Ext.isIE.
So I edited the Ext.History class in ext-all-debug.js and defined the following variable at the top:
var oldIEMode = Ext.isIE6 || Ext.isIE7 || !Ext.isStrict && Ext.isIE8;
There were three conditionals in the class that were checking for Ext.isIE which I replaced with oldIEMode.
I rebuilt and deployed the app and the issue was resolved.
Editing ext-all.js is not best practice, but I should be able to overwrite this class instead.
This is how I ended up solving it:
I created a new patch javascript file and included it after the ext files
/*
#Author: RWR 20130224
This fixes the issue with backward traversal of history in IE8 & higher in standard document mode.
This class was challenging to override (http://www.sencha.com/forum/showthread.php?46306-Help-How-to-extend-Ext.History)
I ended up pasting all of the source original code here and making the necessary changes.
NOTE that this may be patched in version 3.4.1. It is definitely patched in 4.1. When upgrading, validate that this patch is still required.
*/
NewHistory = (function () {
var iframe, hiddenField;
var ready = false;
var currentToken;
var oldIEMode = Ext.isIE6 || Ext.isIE7 || !Ext.isStrict && Ext.isIE8;
function getHash() {
var href = location.href, i = href.indexOf("#"),
hash = i >= 0 ? href.substr(i + 1) : null;
if (Ext.isGecko) {
hash = decodeURIComponent(hash);
}
return hash;
}
function doSave() {
hiddenField.value = currentToken;
}
function handleStateChange(token) {
currentToken = token;
Ext.History.fireEvent('change', token);
}
function updateIFrame (token) {
var html = ['<html><body><div id="state">',Ext.util.Format.htmlEncode(token),'</div></body></html>'].join('');
try {
var doc = iframe.contentWindow.document;
doc.open();
doc.write(html);
doc.close();
return true;
} catch (e) {
return false;
}
}
function checkIFrame() {
if (!iframe.contentWindow || !iframe.contentWindow.document) {
setTimeout(checkIFrame, 10);
return;
}
var doc = iframe.contentWindow.document;
var elem = doc.getElementById("state");
var token = elem ? elem.innerText : null;
var hash = getHash();
setInterval(function () {
doc = iframe.contentWindow.document;
elem = doc.getElementById("state");
var newtoken = elem ? elem.innerText : null;
var newHash = getHash();
if (newtoken !== token) {
token = newtoken;
handleStateChange(token);
location.hash = token;
hash = token;
doSave();
} else if (newHash !== hash) {
hash = newHash;
updateIFrame(newHash);
}
}, 50);
ready = true;
Ext.History.fireEvent('ready', Ext.History);
}
function startUp() {
currentToken = hiddenField.value ? hiddenField.value : getHash();
if (oldIEMode) {
checkIFrame();
} else {
var hash = getHash();
setInterval(function () {
var newHash = getHash();
if (newHash !== hash) {
hash = newHash;
handleStateChange(hash);
doSave();
}
}, 50);
ready = true;
Ext.History.fireEvent('ready', Ext.History);
}
}
return {
/**
* The id of the hidden field required for storing the current history token.
* #type String
* #property s
*/
fieldId: 'x-history-field',
/**
* The id of the iframe required by IE to manage the history stack.
* #type String
* #property s
*/
iframeId: 'x-history-frame',
events:{},
/**
* Initialize the global History instance.
* #param {Boolean} onReady (optional) A callback function that will be called once the history
* component is fully initialized.
* #param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser window.
*/
init: function (onReady, scope) {
if(ready) {
Ext.callback(onReady, scope, [this]);
return;
}
if(!Ext.isReady){
Ext.onReady(function(){
Ext.History.init(onReady, scope);
});
return;
}
hiddenField = Ext.getDom(Ext.History.fieldId);
if (oldIEMode) {
iframe = Ext.getDom(Ext.History.iframeId);
}
this.addEvents(
/**
* #event ready
* Fires when the Ext.History singleton has been initialized and is ready for use.
* #param {Ext.History} The Ext.History singleton.
*/
'ready',
/**
* #event change
* Fires when navigation back or forwards within the local page's history occurs.
* #param {String} token An identifier associated with the page state at that point in its history.
*/
'change'
);
if(onReady){
this.on('ready', onReady, scope, {single:true});
}
startUp();
},
/**
* Add a new token to the history stack. This can be any arbitrary value, although it would
* commonly be the concatenation of a component id and another id marking the specifc history
* state of that component. Example usage:
* <pre><code>
// Handle tab changes on a TabPanel
tabPanel.on('tabchange', function(tabPanel, tab){
Ext.History.add(tabPanel.id + ':' + tab.id);
});
</code></pre>
* #param {String} token The value that defines a particular application-specific history state
* #param {Boolean} preventDuplicates When true, if the passed token matches the current token
* it will not save a new history step. Set to false if the same state can be saved more than once
* at the same history stack location (defaults to true).
*/
add: function (token, preventDup) {
if(preventDup !== false){
if(this.getToken() == token){
return true;
}
}
if (oldIEMode) {
return updateIFrame(token);
} else {
location.hash = token;
return true;
}
},
/**
* Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).
*/
back: function(){
history.go(-1);
},
/**
* Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).
*/
forward: function(){
history.go(1);
},
/**
* Retrieves the currently-active history token.
* #return {String} The token
*/
getToken: function() {
return ready ? currentToken : getHash();
}
};
})();
Ext.apply(NewHistory, new Ext.util.Observable());
Ext.apply(Ext.History, NewHistory);

Resources