LESS mixin for generation media queries - less

I'm trying to create a mixin that automates creation of media queries for predefined screen resolutions and base (template design) width. I've made some workaround and it actually works as acspected, but only for the first call of mixin. Try the code here. I presume that some variable resets is required here to get it done. Here is the code:
// --------------------------------------------------
// VARIABLES
// --------------------------------------------------
// PSD-layout design width
// for default values
#design-width: 1920;
// Media queries
// to recalculate values for
#media-queries: 768, 1024, 1366, 1920;
// --------------------------------------------------
// MIXINS
// --------------------------------------------------
// Construct selector
.add-selector(#selector-name, #selector-properties)
{
#{selector-name}
{
#selector-properties();
}
}
// Generate media queries
.generate-media-queries(#selectors)
{
#length: length(#media-queries);
// First iteration (Mobile First)
.calculation-loop(#selectors, #iterator: 1) when (#iterator = 1)
{
#query-width: extract(#media-queries, #iterator);
#ratio: #query-width / #design-width;
#selectors();
.calculation-loop(#selectors, (#iterator + 1));
}
// Continue iterations (Media Queries)
.calculation-loop(#selectors, #iterator) when (#iterator <= #length)
{
#query-width: extract(#media-queries, #iterator);
#ratio: #query-width / #design-width;
#media(min-width: unit(#query-width, px))
{
#selectors();
}
.calculation-loop(#selectors, (#iterator + 1));
}
.calculation-loop(#selectors);
}
// --------------------------------------------------
// SET SELECTORS
// --------------------------------------------------
// First selector set
#block-1: {
.add-selector(e(".section-1, .section-1>.inner"),
{
width : 50% * #ratio;
});
.add-selector(e(".section-2"),
{
width : 34% * #ratio;
});
};
// Second selector set
#block-2: {
.add-selector(e(".section-3"),
{
width : 81% * #ratio;
height : unit(floor(145 * #ratio), px);
});
};
// --------------------------------------------------
// MIXIN CALLS
// --------------------------------------------------
/* First call
-------------------------------------------------- */
.generate-media-queries(#block-1);
/* Second call
-------------------------------------------------- */
.generate-media-queries(#block-2);
Please, any help be deeply appreciated. Thanks!

Related

Object doesn't support property or method 'map' - IE11

I'm using a Typeahead control searching on a password in Vue which works with no issue and no error in modern browsers, but when testing in IE11 I get the error: 'Object doesn't support property or method 'map''.
Try to add the following javascript script before using the map method (add the script in the head of the index.html page):
// Production steps of ECMA-262, Edition 5, 15.4.4.19
// Reference: http://es5.github.io/#x15.4.4.19
if (!Array.prototype.map) {
Array.prototype.map = function(callback/*, thisArg*/) {
var T, A, k;
if (this == null) {
throw new TypeError('this is null or not defined');
}
// 1. Let O be the result of calling ToObject passing the |this|
// value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get internal
// method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
var len = O.length >>> 0;
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (arguments.length > 1) {
T = arguments[1];
}
// 6. Let A be a new array created as if by the expression new Array(len)
// where Array is the standard built-in constructor with that name and
// len is the value of len.
A = new Array(len);
// 7. Let k be 0
k = 0;
// 8. Repeat, while k < len
while (k < len) {
var kValue, mappedValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal
// method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// ii. Let mappedValue be the result of calling the Call internal
// method of callback with T as the this value and argument
// list containing kValue, k, and O.
mappedValue = callback.call(T, kValue, k, O);
// iii. Call the DefineOwnProperty internal method of A with arguments
// Pk, Property Descriptor
// { Value: mappedValue,
// Writable: true,
// Enumerable: true,
// Configurable: true },
// and false.
// In browsers that support Object.defineProperty, use the following:
// Object.defineProperty(A, k, {
// value: mappedValue,
// writable: true,
// enumerable: true,
// configurable: true
// });
// For best browser support, use the following:
A[k] = mappedValue;
}
// d. Increase k by 1.
k++;
}
// 9. return A
return A;
};
}
More detail information, please refer the Array.prototype.map() method.
Besides, you could also try to add the following script in the head of the index.html page.
<script src='https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/6.23.0/polyfill.min.js'></script>
If still not working, can you post enough code to reproduce the problem in a sample, it might be easier for us to narrow down the problem.

How to implement methods and state for vue.js component

I'm a beginner in VueJS. And as part of my learning process, I'm building a knob for my Pomodoro app. This is my fiddle.
I copied the knob code from codepen, which is implemented using jquery. As you can see in the fiddle most of the job is done by jquery.
I need to try and do this using Vue.js, using its methods and states.
How to refactor this code to a better Vue.JS code? Any suggestions much appreciated.
Vue.component('timer', {
mounted() {
var knob = $('.knob');
var angle = 0;
var minangle = 0;
var maxangle = 270;
var xDirection = "";
var yDirection = "";
var oldX = 0;
var oldY = 0;
function moveKnob(direction) {
if(direction == 'up') {
if((angle + 2) <= maxangle) {
angle = angle + 2;
setAngle();
}
}
else if(direction == 'down') {
if((angle - 2) >= minangle) {
angle = angle - 2;
setAngle();
}
}
}
function setAngle() {
// rotate knob
knob.css({
'-moz-transform':'rotate('+angle+'deg)',
'-webkit-transform':'rotate('+angle+'deg)',
'-o-transform':'rotate('+angle+'deg)',
'-ms-transform':'rotate('+angle+'deg)',
'transform':'rotate('+angle+'deg)'
});
// highlight ticks
var activeTicks = (Math.round(angle / 10) + 1);
$('.tick').removeClass('activetick');
$('.tick').slice(0,activeTicks).addClass('activetick');
// update % value in text
var pc = Math.round((angle/270)*100);
$('.current-value').text(pc+'%');
}
var RAD2DEG = 180 / Math.PI;
knob.centerX = knob.offset().left + knob.width()/2;
knob.centerY = knob.offset().top + knob.height()/2;
var offset, dragging=false;
knob.mousedown(function(e) {
dragging = true;
offset = Math.atan2(knob.centerY - e.pageY, e.pageX - knob.centerX);
})
$(document).mouseup(function() {
dragging = false
})
$(document).mousemove(function(e) {
if (dragging) {
if (oldX < e.pageX) {
xDirection = "right";
} else {
xDirection = "left";
}
oldX = e.pageX;
if(xDirection === "left") {
moveKnob('down');
} else {
moveKnob('up');
}
return false;
}
})
}
});
This example runs without jQuery.
https://jsfiddle.net/guanzo/d6vashmu/6/
Declare all the variables you need in the data function.
Declare all functions under the methods property.
Declare variables that are derived from other variables in the computed property, such as knobStyle, activeTicks, and currentValue, which are all computed from angle. Whenever angle changes, these 3 computed properties will automatically update.
Regarding the general usage of Vue, you should focus on manipulating the data, and letting Vue update the DOM for you.

Titanium Infinite Scroll : Not working

I am trying to add views to my scroll view when it reaches 40% scroll. This is the way I am doing it :
scrollView.add(//add first 10 initial containerView's);
var triggerScroll = true;
var scrollPercentage = 0;
scrollView.addEventListener('scroll', function(e) {
var devHeight = Ti.Platform.displayCaps.platformHeight;
var currPos = scrollView.contentOffset.y;
if(currPos > devHeight){
currPos = currPos - devHeight;
}
scrollPercentage = (currPos)/devHeight * 100;
if(scrollPercentage > 40 && triggerScroll){
triggerScroll = false;
var containerView = myapp.createMyView();
scrollView.add(containerView);
}
//reset scroll to true after the offset reaches end of the screen, so that the
//'scroll' event listener only gets called ONCE every time it crosses 40%
if(scrollPercentage > 101){
triggerScroll = true;
}
});
But its just not working. I am trying to support infinite scroll in my vertical scroll view. Any idea whats going wrong ?
I use the module below when working with infinite scrolling. It use a TableView, but I would think you can apply it to a ScrollView as well. You need to pass in a function that will be called when the TableView register that more content should be loaded.
When you have finished loading you must call the loadingDone-function in order to enable the TableView to initiate another loading sequence.
The value m_bNoDataFound ensure that the loading sequence is not initiated, when there is no more data to fill into the list.
You can alter the offsets (currently 0.75 for Android and 0.90 for iOS) if want the loading sequence to be initiated sooner or later during scroll.
function TableView( onLoad ) {
var isAndroid = Ti.Platform.osname === 'android' ? true : false;
var m_bNoDataFound = false;
var m_nLastDistance = 0;
var m_bPulling = false;
var m_bLoading = false;
var table = Ti.UI.createTableView( {
height : Ti.UI.FILL
} );
table.addEventListener( 'scroll', function( evt ) {
//Scroll to load more data
if( !m_bNoDataFound ) {
if( isAndroid ) {
if( !m_bLoading && ( evt.firstVisibleItem + evt.visibleItemCount ) >= ( evt.totalItemCount * 0.75 ) ) {
onLoad( true );
m_bLoading = true;
}
}
else {
var nTotal = evt.contentOffset.y + evt.size.height;
var nEnd = evt.contentSize.height;
var nDistance = nEnd - nTotal;
if( nDistance < m_nLastDistance ) {
var nNearEnd = nEnd * 0.90;
if( !m_bLoading && ( nTotal >= nNearEnd ) ) {
onLoad( true );
m_bLoading = true;
}
}
m_nLastDistance = nDistance;
}
}
} );
function m_fLoadingDone( a_bNoDataFound ) {
m_bNoDataFound = a_bNoDataFound;
if( m_bLoading )
setTimeout( function( ) {
m_bLoading = false;
}, 250 );
}
return {
table : table,
loadingDone : m_fLoadingDone
};
};
module.exports = TableView;
When integrating an infinite scroll within a scrollview, there are some important things you have to consider:
1. scroll event is triggered a lot: try to throttle your scroll event callback using underscoreJS.
Throttle creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every wait milliseconds. Useful for rate-limiting events that occur faster than you can keep up with. See the underscorejs documentation for more.
2. Default and system units on Android vs iOS: The size of a view on Android uses a different display unit than coordinates of a view. This mismatch in units will cause incorrect calculation of your trigger for the infinite scroll. To solve this, you have to get and set the default unit yourself. The solution can be found in this widget (see getDefaultUnit()): https://github.com/FokkeZB/nl.fokkezb.color/blob/master/controllers/widget.js
3. The ti-infini-scroll can help you with this: this library creates a wrapper around the default Titanium ScrollView. This wrapper contains the calculation of the end of scroll (your trigger for updating/getting new data). When using this library, don't forget to implement bullet number 2.
https://github.com/goodybag/ti-infini-scroll

dojo 1.7 IE9 widget function call not triggering

I'm trying to add a button to a custom pallete to call a function "uiFileInputDlg" which is in the workspace that uses the widget. The upbtn appears on the pallete, but it is not triggering the DoUpload function which is connected in postcreate to then call on "uiFileInputDlg".
works flawlessly in firefox.
we're user dojo 1.7.2
-----------THE TEMPLATE-------------------------
<div class="dijitInline dijitColorPalette">
<div class="dijitColorPaletteInner" data-dojo-attach-point="divNode" role="grid" tabIndex="${tabIndex}">
</div>
<button type="button" id="upbtn"
data-dojo-type="dijit.form.Button"
data-dojo-props="id:'upbtn'"
data-dojo-attach-point="btnUpNode">
Upload New Image
</button>
</div>
-------------------------THE WIDGET--------------------------
//dojo.provide("meemli.UploadPalette");
define([ 'dojo/_base/declare',
'dijit/_WidgetBase',
'dijit/_TemplatedMixin',
'dojo/i18n!dijit/nls/common',
'dojo/text!./templates/UploadPalette.html',
'dijit/_WidgetsInTemplateMixin',
'dojo/_base/lang'
],
function(declare, w, t, i18n, template, witm, lang){
console.log('meemli.UploadPalette: Requiring dijit/nls/common.js INSTEAD OF dojo/nls/common' + i18n.invalidMessage);
return declare("meemli.UploadPalette",
[dijit._Widget, dijit._Templated],
{
// summary: A keyboard accessible color-picking widget
// description:
// Grid showing various colors, so the user can pick a certain color
// Can be used standalone, or as a popup.
//
// example:
// | <div dojoType="dijit.ColorPalette"></div>
//
// example:
// | var picker = new dijit.ColorPalette({ },srcNode);
// | picker.startup();
//
// defaultTimeout: Number
// number of milliseconds before a held key or button becomes typematic
defaultTimeout: 500,
// timeoutChangeRate: Number
// fraction of time used to change the typematic timer between events
// 1.0 means that each typematic event fires at defaultTimeout intervals
// < 1.0 means that each typematic event fires at an increasing faster rate
timeoutChangeRate: 0.90,
// palette: String
// Size of grid, either "7x10" or "3x4".
palette: "3x3",
//_value: String
// The value of the selected color.
value: null,
//_currentFocus: Integer
// Index of the currently focused color.
_currentFocus: 0,
// _xDim: Integer
// This is the number of colors horizontally across.
_xDim: null,
// _yDim: Integer
/// This is the number of colors vertically down.
_yDim: null,
// _palettes: Map
// This represents the value of the colors.
// The first level is a hashmap of the different arrays available
// The next two dimensions represent the columns and rows of colors.
_palettes: {
"3x3": [],
"3x2": ["/images/icons/1.png", "/images/icons/2.png", "/images/icons/3.png","/images/icons/4.png", "/images/icons/5.png", "/images/icons/6.png"]
},
// _imagePaths: Map
// This is stores the path to the palette images
// _imagePaths: {
// "3x3": dojo.moduleUrl("dijit", "templates/icons3x3.png")
// },
// _paletteCoords: Map
// This is a map that is used to calculate the coordinates of the
// images that make up the palette.
_paletteCoords: {
"leftOffset": 3, "topOffset": 3,
"cWidth": 50, "cHeight": 50
},
// templatePath: String
// Path to the template of this widget.
// templateString: dojo.cache("meemli", "templates/UploadPalette.html"),
templateString: template,
// _paletteDims: Object
// Size of the supported palettes for alignment purposes.
_paletteDims: {
"3x3": {"width": "156px", "height": "156px"}, // 48*3 + 3px left/top border + 3px right/bottom border...
"3x2": {"width": "156px", "height": "109px"} // 48*3 + 3px left/top border + 3px right/bottom border...
},
// tabIndex: String
// Widget tabindex.
maxCols: 3,
tabIndex: "0",
_curIndex: 0,
DoUpload: function(){
alert('hello');
uiFileInputDlg(); // function out in the workspace
},
_addImage: function(url) {
row = Math.floor(this._curIndex / this.maxCols);
col = this._curIndex - (row * this.maxCols);
this._curIndex++;
this._yDim = Math.floor(this._curIndex / this.maxCols);
this._xDim = this._curIndex - (row * this.maxCols);
var imgNode = dojo.doc.createElement("img");
imgNode.src = url;
//imgNode.style.height = imgNode.style.width = "48px";
var cellNode = dojo.doc.createElement("span");
cellNode.appendChild(imgNode);
cellNode.connectionRefs = new Array();
dojo.forEach(["Dijitclick", "MouseEnter", "Focus", "Blur"], function(handler) {
cellNode.connectionRefs.push(this.connect(cellNode, "on" + handler.toLowerCase(), "_onCell" + handler));
}, this);
this.divNode.appendChild(cellNode);
var cellStyle = cellNode.style;
cellStyle.top = this._paletteCoords.topOffset + (row * this._paletteCoords.cHeight) + "px";
cellStyle.left = this._paletteCoords.leftOffset + (col * this._paletteCoords.cWidth) + "px";
cellStyle.height = this._paletteCoords.cHeight + "px";
cellStyle.width = this._paletteCoords.cWidth + "px";
// console.debug( "tlhw: " + cellStyle.top + ", " + cellStyle.left + ", " + cellStyle.height + ", " + cellStyle.width );
// adjust size when the bits come...
// this.xh = this.xw = "32px";
//console.log('this.xh => ' + this.xh);
dojo.connect( imgNode,"onload", this, function() {
//console.log('IN: CONNECT...this.xh => ' + this.xh);
this.xh = imgNode.height;
this.xw = imgNode.width;
this.xh = (this.xh==0) ? this.xh="32px" : (this.xh + "");
this.xw = (this.xw==0) ? this.xw="32px" : (this.xw + "");
// var h = parseInt( this.xh );
// var w = parseInt( this.xw );
var hArr = this.xh.split('p');
var wArr = this.xw.split('p');
var h =hArr[0];
var w = wArr[0];
var THUMBNAIL_MAX_WIDTH = 50;
var THUMBNAIL_MAX_HEIGHT = 50;
var hLim = Math.min(THUMBNAIL_MAX_HEIGHT, this._paletteCoords.cHeight);
var wLim = Math.min(THUMBNAIL_MAX_WIDTH, this._paletteCoords.cWidth);
var scale = 1.0;
if( h > hLim || w > wLim ) {
if( h / w < 1.0 ) { // width is bigger than height
scale = wLim / w;
}
else {
scale = hLim / h;
}
}
imgNode.style.height = (scale * h) + "px";
imgNode.style.width = (scale * w) + "px";
console.debug( imgNode.src + ' loaded.'
+ "old: h " + h + ", w " + w + ", scale " + scale
+ ". new: h " + imgNode.style.height + ", w " + imgNode.style.width );
} );
if(dojo.isIE){
// hack to force event firing in IE
// image quirks doc'd in dojox/image/Lightbox.js :: show: function.
// imgNode.src = imgNode.src;
}
dojo.attr(cellNode, "tabindex", "-1");
dojo.addClass(cellNode, "imagePaletteCell");
dijit.setWaiRole(cellNode, "gridcell");
cellNode.index = this._cellNodes.length;
this._cellNodes.push(cellNode);
},
_clearImages: function() {
for(var i = 0; i < this._cellNodes.length; i++) {
this._cellNodes[i].parentNode.removeChild(this._cellNodes[i]);
}
this._currentFocus = 0;
this._curIndex = 0;
this._yDim = 0;
this._xDim = 0;
this._cellNodes = [];
},
setImages: function(imageList) {
this._clearImages();
for(var i = 0; i < imageList.length; i++) {
this._addImage(imageList[i]);
}
},
postCreate: function(){
// A name has to be given to the colorMap, this needs to be unique per Palette.
dojo.mixin(this.divNode.style, this._paletteDims[this.palette]);
// this.imageNode.setAttribute("src", this._imagePaths[this.palette]);
this.domNode.style.position = "relative";
this._cellNodes = [];
this.setImages(this._palettes[this.palette]);
this.connect(this.divNode, "onfocus", "_onDivNodeFocus");
this.connect(this.btnUpNode, "onclick", "DoUpload");
// Now set all events
// The palette itself is navigated to with the tab key on the keyboard
// Keyboard navigation within the Palette is with the arrow keys
// Spacebar selects the color.
// For the up key the index is changed by negative the x dimension.
var keyIncrementMap = {
UP_ARROW: -this._xDim,
// The down key the index is increase by the x dimension.
DOWN_ARROW: this._xDim,
// Right and left move the index by 1.
RIGHT_ARROW: 1,
LEFT_ARROW: -1
};
for(var key in keyIncrementMap){
this._connects.push(dijit.typematic.addKeyListener(this.domNode,
{keyCode:dojo.keys[key], ctrlKey:false, altKey:false, shiftKey:false},
this,
function(){
var increment = keyIncrementMap[key];
return function(count){ this._navigateByKey(increment, count); };
}(),
this.timeoutChangeRate, this.defaultTimeout));
}
},
focus: function(){
// summary:
// Focus this ColorPalette. Puts focus on the first swatch.
this._focusFirst();
},
onChange: function(url, hsz, wsz){
// summary:
// Callback when a image is selected.
// url, hsz, wsz, strings
// height and width string .
// console.debug("img selected: "+url);
},
_focusFirst: function(){
this._currentFocus = 0;
var cellNode = this._cellNodes[this._currentFocus];
window.setTimeout(function(){dijit.focus(cellNode);}, 0);
},
_onDivNodeFocus: function(evt){
// focus bubbles on Firefox 2, so just make sure that focus has really
// gone to the container
if(evt.target === this.divNode){
this._focusFirst();
}
},
_onFocus: function(){
// while focus is on the palette, set its tabindex to -1 so that on a
// shift-tab from a cell, the container is not in the tab order
dojo.attr(this.divNode, "tabindex", "-1");
},
_onBlur: function(){
this._removeCellHighlight(this._currentFocus);
// when focus leaves the palette, restore its tabindex, since it was
// modified by _onFocus().
dojo.attr(this.divNode, "tabindex", this.tabIndex);
},
_onCellDijitclick: function(/*Event*/ evt){
// summary:
// Handler for click, enter key & space key. Selects the color.
// evt:
// The event.
var target = evt.currentTarget;
if (this._currentFocus != target.index){
this._currentFocus = target.index;
window.setTimeout(function(){dijit.focus(target);}, 0);
}
this._selectColor(target);
dojo.stopEvent(evt);
},
_onCellMouseEnter: function(/*Event*/ evt){
// summary:
// Handler for onMouseOver. Put focus on the color under the mouse.
// evt:
// The mouse event.
var target = evt.currentTarget;
window.setTimeout(function(){dijit.focus(target);}, 0);
},
_onCellFocus: function(/*Event*/ evt){
// summary:
// Handler for onFocus. Removes highlight of
// the color that just lost focus, and highlights
// the new color.
// evt:
// The focus event.
this._removeCellHighlight(this._currentFocus);
this._currentFocus = evt.currentTarget.index;
dojo.addClass(evt.currentTarget, "imagePaletteCellHighlight");
},
_onCellBlur: function(/*Event*/ evt){
// summary:
// needed for Firefox 2 on Mac OS X
this._removeCellHighlight(this._currentFocus);
},
_removeCellHighlight: function(index){
dojo.removeClass(this._cellNodes[index], "imagePaletteCellHighlight");
},
_selectColor: function(selectNode){
// summary:
// This selects a color. It triggers the onChange event
// area:
// The area node that covers the color being selected.
var img = selectNode.getElementsByTagName("img")[0];
this.onChange(this.value = img.src, this.xh, this.xw);
},
_navigateByKey: function(increment, typeCount){
// summary:
// This is the callback for typematic.
// It changes the focus and the highlighed color.
// increment:
// How much the key is navigated.
// typeCount:
// How many times typematic has fired.
// typecount == -1 means the key is released.
if(typeCount == -1){ return; }
var newFocusIndex = this._currentFocus + increment;
if(newFocusIndex < this._cellNodes.length && newFocusIndex > -1)
{
var focusNode = this._cellNodes[newFocusIndex];
focusNode.focus();
}
}
});
});
Update
this.connect(this.btnUpNode, "onclick", "DoUpload");
to be
this.connect(this.btnUpNode, "onClick", "DoUpload");
onclick is a dom event, onClick is a dijit event. Since you are using a dijit button you want to use the latter.

How to debug an MSI Custom Action that is implemented in Javascript?

I'm having difficulty figuring out why my Javascript Custom action is failing.
I thought I saw a topic in the WIX.chm file on debugging; now I cannot find it.
Q1
is there doc on how to debug Javascript or VBScript custom actions?
Q2
Is there a way to emit something into the MSI log from a custom action?
Addendum:
Some people think script is the wrong tool for writing CAs.
I don't agree. I think Javascript is a very good tool for the job.
For the doc, look for Session.Message.
To emit messages into the MSI log from a Javascript Custom Action, follow this boilerplate code:
//
// CustomActions.js
//
// Template for WIX Custom Actions written in Javascript.
//
//
// Mon, 23 Nov 2009 10:54
//
// ===================================================================
// http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
var Buttons = {
OkOnly : 0,
OkCancel : 1,
AbortRetryIgnore : 2,
YesNoCancel : 3
};
var Icons = {
Critical : 16,
Question : 32,
Exclamation : 48,
Information : 64
};
var MsgKind = {
Error : 0x01000000,
Warning : 0x02000000,
User : 0x03000000,
Log : 0x04000000
};
// http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
var MsiActionStatus = {
None : 0,
Ok : 1, // success
Cancel : 2,
Abort : 3,
Retry : 4, // aka suspend?
Ignore : 5 // skip remaining actions; this is not an error.
};
function MyCustomActionInJavascript() {
try {
LogMessage("Hello from MyCustomActionInJavascript");
// ...do work here...
LogMessage("Goodbye from MyCustomActionInJavascript");
}
catch (exc1) {
Session.Property("CA_EXCEPTION") = exc1.message ;
LogException(exc1);
return MsiActionStatus.Abort;
}
return MsiActionStatus.Ok;
}
// Pop a message box. also spool a message into the MSI log, if it is enabled.
function LogException(exc) {
var record = Session.Installer.CreateRecord(0);
record.StringData(0) = "CustomAction: Exception: 0x" + decimalToHexString(exc.number) + " : " + exc.message;
Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}
// spool an informational message into the MSI log, if it is enabled.
function LogMessage(msg) {
var record = Session.Installer.CreateRecord(0);
record.StringData(0) = "CustomAction:: " + msg;
Session.Message(MsgKind.Log, record);
}
// popup a msgbox
function AlertUser(msg) {
var record = Session.Installer.CreateRecord(0);
record.StringData(0) = msg;
Session.Message(MsgKind.User + Icons.Information + Buttons.btnOkOnly, record);
}
// Format a number as hex. Quantities over 7ffffff will be displayed properly.
function decimalToHexString(number) {
if (number < 0)
number = 0xFFFFFFFF + number + 1;
return number.toString(16).toUpperCase();
}