<commit_before>/*
 * jQuery UI Dialog
 *
 * Copyright (c) 2008 Richard D. Worth (rdworth.org)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Dialog
 *
 * Depends:
 *	ui.core.js
 *	ui.draggable.js
 *	ui.resizable.js
 *
 */
(function($) {

var setDataSwitch = {
	dragStart: "start.draggable",
	drag: "drag.draggable",
	dragStop: "stop.draggable",
	maxHeight: "maxHeight.resizable",
	minHeight: "minHeight.resizable",
	maxWidth: "maxWidth.resizable",
	minWidth: "minWidth.resizable",
	resizeStart: "start.resizable",
	resize: "drag.resizable",
	resizeStop: "stop.resizable"
};

$.widget("ui.dialog", {
	init: function() {
		var self = this,
			options = this.options,
			resizeHandles = typeof options.resizable == 'string'
				? options.resizable
				: 'n,e,s,w,se,sw,ne,nw',

			uiDialogContent = this.element
				.addClass('ui-dialog-content')
				.wrap('
')
				.wrap('
'),

			uiDialogContainer = uiDialogContent.parent()
				.addClass('ui-dialog-container')
				.css({position: 'relative'}),

			title = options.title || uiDialogContent.attr('title') || '',
			uiDialogTitlebar = (this.uiDialogTitlebar =
				$('
'))
				.append('' + title + '')
				.append('X')
				.prependTo(uiDialogContainer),

			uiDialog = (this.uiDialog = uiDialogContainer.parent())
				.appendTo(document.body)
				.hide()
				.addClass('ui-dialog')
				.addClass(options.dialogClass)
				// add content classes to dialog
				// to inherit theme at top level of element
				.addClass(uiDialogContent.attr('className'))
					.removeClass('ui-dialog-content')
				.css({
					position: 'absolute',
					width: options.width,
					height: options.height,
					overflow: 'hidden',
					zIndex: options.zIndex
				})
				// setting tabIndex makes the div focusable
				// setting outline to 0 prevents a border on focus in Mozilla
				.attr('tabIndex', -1).css('outline', 0).keydown(function(ev) {
					if (options.closeOnEscape) {
						var ESC = 27;
						(ev.keyCode && ev.keyCode == ESC && self.close());
					}
				})
				.mousedown(function() {
					self.moveToTop();
				});

		this.uiDialogTitlebarClose = $('.ui-dialog-titlebar-close', uiDialogTitlebar)
			.hover(
				function() {
					$(this).addClass('ui-dialog-titlebar-close-hover');
				},
				function() {
					$(this).removeClass('ui-dialog-titlebar-close-hover');
				}
			)
			.mousedown(function(ev) {
				ev.stopPropagation();
			})
			.click(function() {
				self.close();
				return false;
			});

		var hasButtons = false;
		$.each(options.buttons, function() { return !(hasButtons = true); });
		if (hasButtons) {
			var uiDialogButtonPane = $('
')
				.appendTo(uiDialog);
			$.each(options.buttons, function(name, fn) {
				$('<button/>')
					.text(name)
					.click(function() { fn.apply(self.element[0], arguments); })
					.appendTo(uiDialogButtonPane);
			});
		}

		if ($.fn.draggable) {
			uiDialog.draggable({
				handle: '.ui-dialog-titlebar',
				start: function(e, ui) {
					self.moveToTop();
					(options.dragStart && options.dragStart.apply(this, arguments));
				},
				drag: options.drag,
				stop: function(e, ui) {
					(options.dragStop && options.dragStop.apply(this, arguments));
					$.ui.dialog.overlay.resize();
				}
			});
			(options.draggable || uiDialog.draggable('disable'));
		}

		if ($.fn.resizable) {
			uiDialog.resizable({
				maxWidth: options.maxWidth,
				maxHeight: options.maxHeight,
				minWidth: options.minWidth,
				minHeight: options.minHeight,
				start: options.resizeStart,
				resize: options.resize,
				handles: resizeHandles,
				stop: function(e, ui) {
					(options.resizeStop && options.resizeStop.apply(this, arguments));
					$.ui.dialog.overlay.resize();
				}
			});
			(options.resizable || uiDialog.resizable('disable'));
		}

		(options.bgiframe && $.fn.bgiframe && uiDialog.bgiframe());
		(options.autoOpen && this.open());
	},

	setData: function(key, value){
		(setDataSwitch[key] && this.uiDialog.data(setDataSwitch[key], value));
		switch (key) {
			case "draggable":
				this.uiDialog.draggable(value ? 'enable' : 'disable');
				break;
			case "height":
				this.uiDialog.height(value);
				break;
			case "position":
				this.position(value);
				break;
			case "resizable":
				(typeof value == 'string' && this.uiDialog.data('handles.resizable', value));
				this.uiDialog.resizable(value ? 'enable' : 'disable');
				break;
			case "title":
				$(".ui-dialog-title", this.uiDialogTitlebar).text(value);
				break;
			case "width":
				this.uiDialog.width(value);
				break;
		}

		$.widget.prototype.setData.apply(this, arguments);
	},

	position: function(pos) {
		var wnd = $(window), doc = $(document),
			pTop = doc.scrollTop(), pLeft = doc.scrollLeft(),
			minTop = pTop;

		if ($.inArray(pos, ['center','top','right','bottom','left']) >= 0) {
			pos = [
				pos == 'right' || pos == 'left' ? pos : 'center',
				pos == 'top' || pos == 'bottom' ? pos : 'middle'
			];
		}
		if (pos.constructor != Array) {
			pos = ['center', 'middle'];
		}
		if (pos[0].constructor == Number) {
			pLeft += pos[0];
		} else {
			switch (pos[0]) {
				case 'left':
					pLeft += 0;
					break;
				case 'right':
					pLeft += wnd.width() - this.uiDialog.width();
					break;
				default:
				case 'center':
					pLeft += (wnd.width() - this.uiDialog.width()) / 2;
			}
		}
		if (pos[1].constructor == Number) {
			pTop += pos[1];
		} else {
			switch (pos[1]) {
				case 'top':
					pTop += 0;
					break;
				case 'bottom':
					pTop += wnd.height() - this.uiDialog.height();
					break;
				default:
				case 'middle':
					pTop += (wnd.height() - this.uiDialog.height()) / 2;
			}
		}

		// prevent the dialog from being too high (make sure the titlebar
		// is accessible)
		pTop = Math.max(pTop, minTop);
		this.uiDialog.css({top: pTop, left: pLeft});
	},

	open: function() {
		this.overlay = this.options.modal ? new $.ui.dialog.overlay(this) : null;
		this.uiDialog.appendTo('body');
		this.position(this.options.position);
		this.uiDialog.show();
		this.moveToTop(true);

		// CALLBACK: open
		var openEV = null;
		var openUI = {
			options: this.options
		};
		this.uiDialogTitlebarClose.focus();
		this.element.triggerHandler("dialogopen", [openEV, openUI], this.options.open);
	},

	// the force parameter allows us to move modal dialogs to their correct
	// position on open
	moveToTop: function(force) {
		if ((this.options.modal && !force) || !this.options.stack) { return; }

		var maxZ = this.options.zIndex, options = this.options;
		$('.ui-dialog:visible').each(function() {
			maxZ = Math.max(maxZ, parseInt($(this).css('z-index'), 10) || options.zIndex);
		});
		(this.overlay && this.overlay.$el.css('z-index', ++maxZ));
		this.uiDialog.css('z-index', ++maxZ);
	},

	close: function() {
		(this.overlay && this.overlay.destroy());
		this.uiDialog.hide();

		// CALLBACK: close
		var closeEV = null;
		var closeUI = {
			options: this.options
		};
		this.element.triggerHandler("dialogclose", [closeEV, closeUI], this.options.close);
		$.ui.dialog.overlay.resize();
	},

	destroy: function() {
		(this.overlay && this.overlay.destroy());
		this.uiDialog.hide();
		this.element
			.unbind('.dialog')
			.removeData('dialog')
			.removeClass('ui-dialog-content')
			.hide().appendTo('body');
		this.uiDialog.remove();
	}
});

$.extend($.ui.dialog, {
	defaults: {
		autoOpen: true,
		bgiframe: false,
		buttons: {},
		closeOnEscape: true,
		draggable: true,
		height: 200,
		minHeight: 100,
		minWidth: 150,
		modal: false,
		overlay: {},
		position: 'center',
		resizable: true,
		stack: true,
		width: 300,
		zIndex: 1000
	},

	overlay: function(dialog) {
		this.$el = $.ui.dialog.overlay.create(dialog);
	}
});

$.extend($.ui.dialog.overlay, {
	instances: [],
	events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
		function(e) { return e + '.dialog-overlay'; }).join(' '),
	create: function(dialog) {
		if (this.instances.length === 0) {
			// prevent use of anchors and inputs
			// we use a setTimeout in case the overlay is created from an
			// event that we're going to be cancelling (see #2804)
			setTimeout(function() {
				$('a, :input').bind($.ui.dialog.overlay.events, function() {
					// allow use of the element if inside a dialog and
					// - there are no modal dialogs
					// - there are modal dialogs, but we are in front of the topmost modal
					var allow = false;
					var $dialog = $(this).parents('.ui-dialog');
					if ($dialog.length) {
						var $overlays = $('.ui-dialog-overlay');
						if ($overlays.length) {
							var maxZ = parseInt($overlays.css('z-index'), 10);
							$overlays.each(function() {
								maxZ = Math.max(maxZ, parseInt($(this).css('z-index'), 10));
							});
							allow = parseInt($dialog.css('z-index'), 10) > maxZ;
						} else {
							allow = true;
						}
					}
					return allow;
				});
			}, 1);

			// allow closing by pressing the escape key
			$(document).bind('keydown.dialog-overlay', function(e) {
				var ESC = 27;
				(e.keyCode && e.keyCode == ESC && dialog.close());
			});

			// handle window resize
			$(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize);
		}

		var $el = $('
').appendTo(document.body)
			.addClass('ui-dialog-overlay').css($.extend({
				borderWidth: 0, margin: 0, padding: 0,
				position: 'absolute', top: 0, left: 0,
				width: this.width(),
				height: this.height()
			}, dialog.options.overlay));

		(dialog.options.bgiframe && $.fn.bgiframe && $el.bgiframe());

		this.instances.push($el);
		return $el;
	},

	destroy: function($el) {
		this.instances.splice($.inArray(this.instances, $el), 1);

		if (this.instances.length === 0) {
			$('a, :input').add([document, window]).unbind('.dialog-overlay');
		}

		$el.remove();
	},

	height: function() {
		if ($.browser.msie && $.browser.version < 7) {
			var scrollHeight = Math.max(
				document.documentElement.scrollHeight,
				document.body.scrollHeight
			);
			var offsetHeight = Math.max(
				document.documentElement.offsetHeight,
				document.body.offsetHeight
			);

			if (scrollHeight < offsetHeight) {
				return $(window).height() + 'px';
			} else {
				return scrollHeight + 'px';
			}
		} else {
			return $(document).height() + 'px';
		}
	},

	width: function() {
		if ($.browser.msie && $.browser.version < 7) {
			var scrollWidth = Math.max(
				document.documentElement.scrollWidth,
				document.body.scrollWidth
			);
			var offsetWidth = Math.max(
				document.documentElement.offsetWidth,
				document.body.offsetWidth
			);

			if (scrollWidth < offsetWidth) {
				return $(window).width() + 'px';
			} else {
				return scrollWidth + 'px';
			}
		} else {
			return $(document).width() + 'px';
		}
	},

	resize: function() {
		/* If the dialog is draggable and the user drags it past the
		 * right edge of the window, the document becomes wider so we
		 * need to stretch the overlay. If the user then drags the
		 * dialog back to the left, the document will become narrower,
		 * so we need to shrink the overlay to the appropriate size.
		 * This is handled by shrinking the overlay before setting it
		 * to the full document size.
		 */
		var $overlays = $([]);
		$.each($.ui.dialog.overlay.instances, function() {
			$overlays = $overlays.add(this);
		});

		$overlays.css({
			width: 0,
			height: 0
		}).css({
			width: $.ui.dialog.overlay.width(),
			height: $.ui.dialog.overlay.height()
		});
	}
});

$.extend($.ui.dialog.overlay.prototype, {
	destroy: function() {
		$.ui.dialog.overlay.destroy(this.$el);
	}
});

})(jQuery);
<commit_msg>Partial fix for #2538 - still need to add support for options<commit_after>/*
 * jQuery UI Dialog
 *
 * Copyright (c) 2008 Richard D. Worth (rdworth.org)
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * http://docs.jquery.com/UI/Dialog
 *
 * Depends:
 *	ui.core.js
 *	ui.draggable.js
 *	ui.resizable.js
 *
 */
(function($) {

var setDataSwitch = {
	dragStart: "start.draggable",
	drag: "drag.draggable",
	dragStop: "stop.draggable",
	maxHeight: "maxHeight.resizable",
	minHeight: "minHeight.resizable",
	maxWidth: "maxWidth.resizable",
	minWidth: "minWidth.resizable",
	resizeStart: "start.resizable",
	resize: "drag.resizable",
	resizeStop: "stop.resizable"
};

$.widget("ui.dialog", {
	init: function() {
		var self = this,
			options = this.options,
			resizeHandles = typeof options.resizable == 'string'
				? options.resizable
				: 'n,e,s,w,se,sw,ne,nw',

			uiDialogContent = this.element
				.addClass('ui-dialog-content')
				.wrap('
')
				.wrap('
'),

			uiDialogContainer = uiDialogContent.parent()
				.addClass('ui-dialog-container')
				.css({position: 'relative'}),

			title = options.title || uiDialogContent.attr('title') || '',
			uiDialogTitlebar = (this.uiDialogTitlebar =
				$('
'))
				.append('' + title + '')
				.append('X')
				.prependTo(uiDialogContainer),

			uiDialog = (this.uiDialog = uiDialogContainer.parent())
				.appendTo(document.body)
				.hide()
				.addClass('ui-dialog')
				.addClass(options.dialogClass)
				// add content classes to dialog
				// to inherit theme at top level of element
				.addClass(uiDialogContent.attr('className'))
					.removeClass('ui-dialog-content')
				.css({
					position: 'absolute',
					width: options.width,
					height: options.height,
					overflow: 'hidden',
					zIndex: options.zIndex
				})
				// setting tabIndex makes the div focusable
				// setting outline to 0 prevents a border on focus in Mozilla
				.attr('tabIndex', -1).css('outline', 0).keydown(function(ev) {
					if (options.closeOnEscape) {
						var ESC = 27;
						(ev.keyCode && ev.keyCode == ESC && self.close());
					}
				})
				.mousedown(function() {
					self.moveToTop();
				});

		this.uiDialogTitlebarClose = $('.ui-dialog-titlebar-close', uiDialogTitlebar)
			.hover(
				function() {
					$(this).addClass('ui-dialog-titlebar-close-hover');
				},
				function() {
					$(this).removeClass('ui-dialog-titlebar-close-hover');
				}
			)
			.mousedown(function(ev) {
				ev.stopPropagation();
			})
			.click(function() {
				self.close();
				return false;
			});

		var hasButtons = false;
		$.each(options.buttons, function() { return !(hasButtons = true); });
		if (hasButtons) {
			var uiDialogButtonPane = $('
')
				.appendTo(uiDialog);
			$.each(options.buttons, function(name, fn) {
				$('<button/>')
					.text(name)
					.click(function() { fn.apply(self.element[0], arguments); })
					.appendTo(uiDialogButtonPane);
			});
		}

		if ($.fn.draggable) {
			uiDialog.draggable({
				handle: '.ui-dialog-titlebar',
				start: function(e, ui) {
					self.moveToTop();
					(options.dragStart && options.dragStart.apply(this, arguments));
				},
				drag: options.drag,
				stop: function(e, ui) {
					(options.dragStop && options.dragStop.apply(this, arguments));
					$.ui.dialog.overlay.resize();
				}
			});
			(options.draggable || uiDialog.draggable('disable'));
		}

		if ($.fn.resizable) {
			uiDialog.resizable({
				maxWidth: options.maxWidth,
				maxHeight: options.maxHeight,
				minWidth: options.minWidth,
				minHeight: options.minHeight,
				start: options.resizeStart,
				resize: options.resize,
				handles: resizeHandles,
				stop: function(e, ui) {
					(options.resizeStop && options.resizeStop.apply(this, arguments));
					$.ui.dialog.overlay.resize();
				}
			});
			(options.resizable || uiDialog.resizable('disable'));
		}

		(options.bgiframe && $.fn.bgiframe && uiDialog.bgiframe());
		(options.autoOpen && this.open());
	},

	setData: function(key, value){
		(setDataSwitch[key] && this.uiDialog.data(setDataSwitch[key], value));
		switch (key) {
			case "draggable":
				this.uiDialog.draggable(value ? 'enable' : 'disable');
				break;
			case "height":
				this.uiDialog.height(value);
				break;
			case "position":
				this.position(value);
				break;
			case "resizable":
				(typeof value == 'string' && this.uiDialog.data('handles.resizable', value));
				this.uiDialog.resizable(value ? 'enable' : 'disable');
				break;
			case "title":
				$(".ui-dialog-title", this.uiDialogTitlebar).text(value);
				break;
			case "width":
				this.uiDialog.width(value);
				break;
		}

		$.widget.prototype.setData.apply(this, arguments);
	},

	position: function(pos) {
		var wnd = $(window), doc = $(document),
			pTop = doc.scrollTop(), pLeft = doc.scrollLeft(),
			minTop = pTop;

		if ($.inArray(pos, ['center','top','right','bottom','left']) >= 0) {
			pos = [
				pos == 'right' || pos == 'left' ? pos : 'center',
				pos == 'top' || pos == 'bottom' ? pos : 'middle'
			];
		}
		if (pos.constructor != Array) {
			pos = ['center', 'middle'];
		}
		if (pos[0].constructor == Number) {
			pLeft += pos[0];
		} else {
			switch (pos[0]) {
				case 'left':
					pLeft += 0;
					break;
				case 'right':
					pLeft += wnd.width() - this.uiDialog.width();
					break;
				default:
				case 'center':
					pLeft += (wnd.width() - this.uiDialog.width()) / 2;
			}
		}
		if (pos[1].constructor == Number) {
			pTop += pos[1];
		} else {
			switch (pos[1]) {
				case 'top':
					pTop += 0;
					break;
				case 'bottom':
					pTop += wnd.height() - this.uiDialog.height();
					break;
				default:
				case 'middle':
					pTop += (wnd.height() - this.uiDialog.height()) / 2;
			}
		}

		// prevent the dialog from being too high (make sure the titlebar
		// is accessible)
		pTop = Math.max(pTop, minTop);
		this.uiDialog.css({top: pTop, left: pLeft});
	},

	open: function() {
		this.overlay = this.options.modal ? new $.ui.dialog.overlay(this) : null;
		this.uiDialog.appendTo('body');
		this.position(this.options.position);
		this.uiDialog.show(this.options.show);
		this.moveToTop(true);

		// CALLBACK: open
		var openEV = null;
		var openUI = {
			options: this.options
		};
		this.uiDialogTitlebarClose.focus();
		this.element.triggerHandler("dialogopen", [openEV, openUI], this.options.open);
	},

	// the force parameter allows us to move modal dialogs to their correct
	// position on open
	moveToTop: function(force) {
		if ((this.options.modal && !force) || !this.options.stack) { return; }

		var maxZ = this.options.zIndex, options = this.options;
		$('.ui-dialog:visible').each(function() {
			maxZ = Math.max(maxZ, parseInt($(this).css('z-index'), 10) || options.zIndex);
		});
		(this.overlay && this.overlay.$el.css('z-index', ++maxZ));
		this.uiDialog.css('z-index', ++maxZ);
	},

	close: function() {
		(this.overlay && this.overlay.destroy());
		this.uiDialog.hide(this.options.hide);

		// CALLBACK: close
		var closeEV = null;
		var closeUI = {
			options: this.options
		};
		this.element.triggerHandler("dialogclose", [closeEV, closeUI], this.options.close);
		$.ui.dialog.overlay.resize();
	},

	destroy: function() {
		(this.overlay && this.overlay.destroy());
		this.uiDialog.hide();
		this.element
			.unbind('.dialog')
			.removeData('dialog')
			.removeClass('ui-dialog-content')
			.hide().appendTo('body');
		this.uiDialog.remove();
	}
});

$.extend($.ui.dialog, {
	defaults: {
		autoOpen: true,
		bgiframe: false,
		buttons: {},
		closeOnEscape: true,
		draggable: true,
		height: 200,
		minHeight: 100,
		minWidth: 150,
		modal: false,
		overlay: {},
		position: 'center',
		resizable: true,
		stack: true,
		width: 300,
		zIndex: 1000
	},

	overlay: function(dialog) {
		this.$el = $.ui.dialog.overlay.create(dialog);
	}
});

$.extend($.ui.dialog.overlay, {
	instances: [],
	events: $.map('focus,mousedown,mouseup,keydown,keypress,click'.split(','),
		function(e) { return e + '.dialog-overlay'; }).join(' '),
	create: function(dialog) {
		if (this.instances.length === 0) {
			// prevent use of anchors and inputs
			// we use a setTimeout in case the overlay is created from an
			// event that we're going to be cancelling (see #2804)
			setTimeout(function() {
				$('a, :input').bind($.ui.dialog.overlay.events, function() {
					// allow use of the element if inside a dialog and
					// - there are no modal dialogs
					// - there are modal dialogs, but we are in front of the topmost modal
					var allow = false;
					var $dialog = $(this).parents('.ui-dialog');
					if ($dialog.length) {
						var $overlays = $('.ui-dialog-overlay');
						if ($overlays.length) {
							var maxZ = parseInt($overlays.css('z-index'), 10);
							$overlays.each(function() {
								maxZ = Math.max(maxZ, parseInt($(this).css('z-index'), 10));
							});
							allow = parseInt($dialog.css('z-index'), 10) > maxZ;
						} else {
							allow = true;
						}
					}
					return allow;
				});
			}, 1);

			// allow closing by pressing the escape key
			$(document).bind('keydown.dialog-overlay', function(e) {
				var ESC = 27;
				(e.keyCode && e.keyCode == ESC && dialog.close());
			});

			// handle window resize
			$(window).bind('resize.dialog-overlay', $.ui.dialog.overlay.resize);
		}

		var $el = $('
').appendTo(document.body)
			.addClass('ui-dialog-overlay').css($.extend({
				borderWidth: 0, margin: 0, padding: 0,
				position: 'absolute', top: 0, left: 0,
				width: this.width(),
				height: this.height()
			}, dialog.options.overlay));

		(dialog.options.bgiframe && $.fn.bgiframe && $el.bgiframe());

		this.instances.push($el);
		return $el;
	},

	destroy: function($el) {
		this.instances.splice($.inArray(this.instances, $el), 1);

		if (this.instances.length === 0) {
			$('a, :input').add([document, window]).unbind('.dialog-overlay');
		}

		$el.remove();
	},

	height: function() {
		if ($.browser.msie && $.browser.version < 7) {
			var scrollHeight = Math.max(
				document.documentElement.scrollHeight,
				document.body.scrollHeight
			);
			var offsetHeight = Math.max(
				document.documentElement.offsetHeight,
				document.body.offsetHeight
			);

			if (scrollHeight < offsetHeight) {
				return $(window).height() + 'px';
			} else {
				return scrollHeight + 'px';
			}
		} else {
			return $(document).height() + 'px';
		}
	},

	width: function() {
		if ($.browser.msie && $.browser.version < 7) {
			var scrollWidth = Math.max(
				document.documentElement.scrollWidth,
				document.body.scrollWidth
			);
			var offsetWidth = Math.max(
				document.documentElement.offsetWidth,
				document.body.offsetWidth
			);

			if (scrollWidth < offsetWidth) {
				return $(window).width() + 'px';
			} else {
				return scrollWidth + 'px';
			}
		} else {
			return $(document).width() + 'px';
		}
	},

	resize: function() {
		/* If the dialog is draggable and the user drags it past the
		 * right edge of the window, the document becomes wider so we
		 * need to stretch the overlay. If the user then drags the
		 * dialog back to the left, the document will become narrower,
		 * so we need to shrink the overlay to the appropriate size.
		 * This is handled by shrinking the overlay before setting it
		 * to the full document size.
		 */
		var $overlays = $([]);
		$.each($.ui.dialog.overlay.instances, function() {
			$overlays = $overlays.add(this);
		});

		$overlays.css({
			width: 0,
			height: 0
		}).css({
			width: $.ui.dialog.overlay.width(),
			height: $.ui.dialog.overlay.height()
		});
	}
});

$.extend($.ui.dialog.overlay.prototype, {
	destroy: function() {
		$.ui.dialog.overlay.destroy(this.$el);
	}
});

})(jQuery);
<|endoftext|>


<commit_before>var parse = require("pug-parser");
var lex = require("pug-lexer");
var Block = require("./block.js");
var tags = "a abbr address area article aside audio b base bdi bdo big blockquote body br button canvas caption cite code col colgroup data datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hr html i iframe img input ins kbd keygen label legend li link main map mark menu menuitem meta meter nav noscript object ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strong style sub summary sup table tbody td textarea tfoot th thead time title tr track u ul var video wbr circle defs ellipse g line linearGradient mask path pattern polygon polyline radialGradient rect stop svg text tspan".split(" ");

function Compilation(doc,path){
	doc = doc.replace(/\r\n/g,"\n");
    this.path = path;
    this.tree = parse(lex(doc)).nodes;
    this.main = new Block();
    this.mixins = new Block();
    this.script = new Block();
	this.imports = new Block();
	this.members = new Block();
    this.extendblock = new Block();
    this.blocks = new Block();
    this.block = this.main;
    this.blocklist = {};
}

Compilation.prototype = Object.create(Block.prototype);

Compilation.prototype.compile = function(){
    this.deblock(this.tree);
    this.tree.forEach(function(node){
        if(node.type == "Tag" && node.name == "script") node.name = "main";
    })
	this.block.writeLine('"use strict";');
    this.block.writeLine("var React = require(\"react\");");
    this.block.writeLine("var jade2react = require(\"jade2react\");");
	this.block.writeBlock(this.imports);
    this.block.writeLine("");
    this.block.writeBlock(this.extendblock); //start of class
    this.block.indent(1);
    this.block.writeLine("_render(__add){",1);
    this.renderNodes(this.tree,true);
    if(this.extendblock.out.length){
        this.block.writeLine("super._render.call(this,__add);");
    }
    this.block.indent(-1);
    this.block.writeLine("}");
    this.block.writeLine("render(){",1);
    this.block.writeLine("return jade2react.render(this,this._render)[0];",-1);
    this.block.writeLine("}");
    for(var block in this.blocklist){
        var b = this.blocklist[block];
        this.block.writeLine(block+"(__add){",1);
        if(b.prepend){
            this.renderNodes(b.prepend.nodes);
        }
        if(b.replace){
            this.renderNodes(b.replace.nodes);
        }else{
            this.block.writeLine("super."+block+".call(this,__add);");
        }
        if(b.append){
            this.renderNodes(b.append.nodes);
        }
        this.block.indent(-1);
        this.block.writeLine("}");
    }
    if(this.mixins.out.length){
        this.block.writeBlock(this.mixins);
    }
	if(this.members.out.length){
		this.block.writeBlock(this.members);
	}
    this.block.indent(-1);
    this.block.writeLine("}"); //end of class
	this.block.writeLine("module.exports = Component;");
    this.block.writeLine("");
    this.block.writeBlock(this.script);

    if(!this.extendblock.out.length){
        this.renderExtends()
    }

    var code = this.block.build();
    return code;
}

Compilation.prototype.deblock = function(nodes){
    for(var i = 0; i < nodes.length; i++){
        var node = nodes[i];
        if(node.block) node.nodes = node.block.nodes;
        if(node.nodes) this.deblock(node.nodes);
    }
}

Compilation.prototype.renderNode = function(node){
    switch(node.type){
        case "Text":
        case "Code":
        case "Tag":
        case "Block":
        case "Each":
        case "Comment":
        case "BlockComment":
        case "Case":
        case "When":
        case "Mixin":
        case "MixinBlock":
        case "Extends":
        case "NamedBlock":
            this["render"+node.type](node);
            break;
    }
}

Compilation.prototype.renderNodes = function(nodes){
    for(var i = 0; i < nodes.length; i++){
        this.renderNode(nodes[i]);
    }
}

Compilation.prototype.renderText = function(node){
    if(!node.val) return;
    this.block.writeLine("__add(\""+node.val+"\");");
}

Compilation.prototype.renderCode = function(node){
    if(!node.val) return;
    if(node.block){
        this.block.writeLine(node.val+"{",1);
        this.renderNodes(node.block.nodes);
        this.block.indent(-1);
        this.block.writeLine("}");
    }else if(node.buffer){
        this.block.writeLine("__add("+node.val+");");
    }else{
        this.block.writeLine(node.val);
    }
}

Compilation.prototype.renderTag = function(node){
    if(node.name=="main"){
		var target = this.script;
		for(var i = 0; i < node.attrs.length; i++){
			if(node.attrs[i].name == "section"){
				switch(node.attrs[i].val){
					case "start":
					case "'start'":
					case '"start"':
						var target = this.imports;
						break;
					case "body":
					case "'body'":
					case '"body"':
						var target = this.members;
						break;
					case "end":
					case "'end'":
					case '"end"':
					default:
						var target = this.script;
						break;
				}
				break;
			}
		}
		var content = [];
        for(var i = 0; i < node.nodes.length; i++){
            switch(node.nodes[i].type){
                case "Text":
                    content.push(node.nodes[i].val);
                    break;
            }
        }
		content.join("").split("\n").forEach(function(line){
			target.writeLine(line);
		});
    }else if(node.name == "children"){
        this.block.writeLine("for(var i = 0; i < this.props.children.length; i++){",1);
        this.block.writeLine("__add(this.props.children[i]);",-1);
        this.block.writeLine("}");
    }else{
        var isDOM = tags.indexOf(node.name)>=0;
        this.block.write("__add(React.createFactory("+(isDOM?"'":"")+node.name+(isDOM?"'":"")+"),");

        if(node.code && node.code.val){
            node.nodes.unshift(node.code)
        }
        if(node.name == "script" || node.name == "style"){
            var script = [];
            for(var i = 0; i < node.nodes.length; i++){
                switch(node.nodes[i].type){
                    case "Code":
                        script.push(node.nodes[i].val);
                        break;
                    case "Text":
                        script.push("\""+node.nodes[i].val.replace(/"/g,"\\\"").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"\"");
                        break;
                }
            }
            script = script.join(" + ");
            if(script){
                node.attrs.push({
                    name:"dangerouslySetInnerHTML",
                    val:"{__html:"+script+"}"
                })
            }
            node.nodes = [];
        }
        this.renderAttributes(node);

        if(node.nodes.length){
            this.block.writeLine(",function(__add){",1);
            this.renderNodes(node.nodes);
            this.block.indent(-1);
            this.block.writeLine("});");
        }else{
            this.block.writeLine(");");
        }
    }
}

Compilation.prototype.renderBlock = function(node){
    this.renderNodes(node.nodes);
}

Compilation.prototype.renderAttributes = function(node){
    if(node.attrs){
        var attributes = {};
        for(var i = 0; i < node.attrs.length; i++){
            var att = node.attrs[i];
            if(attributes[att.name] && att.name == "class"){
                attributes[att.name] += " + \" \" + "+att.val;
            }else{
                attributes[att.name] = att.val;
            }
        }

        if(attributes.class){
            attributes.className = attributes.class;
            delete attributes.class;
        }
        if(node.attributeBlocks.length) this.block.write("jade2react.mixinAttributes(");
        var first = true;
        this.block.write("{");
        for(var att in attributes){
            this.block.write((first?"":",")+'"'+att+'":'+(att=="style"?"jade2react.mapStyle(":"")+attributes[att]+(att=="style"?")":""));
            first = false;
        }
        this.block.write("}");
        if(node.attributeBlocks.length) this.block.write(",["+node.attributeBlocks.join(",")+"])");
    }else{
        this.block.write("{}");
    }
}

Compilation.prototype.renderEach = function(node){
    this.block.writeLine("for(var "+(node.key||"__key")+" in "+node.obj+"){",1);
    if(node.val){
        this.block.writeLine("var "+node.val+" = "+node.obj+"["+(node.key||"__key")+"];");
    }
    this.renderNodes(node.nodes);
    this.block.indent(-1);
    this.block.writeLine("}");
}

Compilation.prototype.renderComment = function(node){
    this.block.writeLine("//"+node.val)
}

Compilation.prototype.renderBlockComment = function(node){
    this.block.writeLine("/*",1);
    for(var i = 0; i < node.nodes.length; i++){
        this.block.writeLine(node.nodes[i].val);
    }
    this.block.indent(-1);
    this.block.writeLine("*/");
}

Compilation.prototype.renderCase = function(node){
    this.block.writeLine("switch("+node.expr+"){",1);
    this.renderNodes(node.nodes);
    this.block.indent(-1);
    this.block.writeLine("}");
}

Compilation.prototype.renderWhen = function(node){
    this.block.writeLine("case "+node.expr+":",1);
    if(node.nodes){
        this.renderNodes(node.nodes);
        this.block.writeLine("break;");
    }
    this.block.indent(-1);
}

Compilation.prototype.renderMixin = function(node){
    if(node.call){
        this.block.write("this."+node.name+"(");
        if(node.nodes){
            this.block.writeLine("jade2react.render(this,function(__add){",1);
            this.renderNodes(node.nodes);
            this.block.indent(-1);
            this.block.write("}),");
        }else{
            this.block.write("[],");
        }
        this.renderAttributes(node);
        this.block.writeLine(","+node.args+").forEach(__add);");
    }else{
        var prev = this.block;
        this.block = new Block();
        this.mixins.writeBlock(this.block);

        if(node.args){
            var argparts = node.args.split("...");
            argparts[0] = argparts[0].trim();
            if(argparts[0][argparts[0].length-1] == ","){
                argparts[0] = argparts[0].slice(0,-1);
            }
            argparts[0] = ","+argparts[0];
        }else{
            argparts = [""];
        }

        this.block.writeLine("Component.prototype."+node.name+" = function(block,attributes"+argparts[0]+"){",1);
        if(argparts.length > 1){
            this.block.writeLine("var "+argparts[1].trim()+" = Array.prototype.slice.call(arguments,"+argparts[0].split(",").length+");");
        }
        this.block.writeLine("return jade2react.render(this,function(__add){",1);
        this.renderNodes(node.nodes)
        this.block.indent(-1);
        this.block.writeLine("});");
        this.block.indent(-1);
        this.block.writeLine("}");
        this.block = prev;
    }
}

Compilation.prototype.renderMixinBlock = function(node){
    this.block.writeLine("block.forEach(__add)")
}

Compilation.prototype.renderExtends = function(node){
    var baseComponent = (node?("require(\""+node.file.path+"\")"):"React.Component");
    this.extendblock.writeLine("class Component extends "+baseComponent+"{");
}

Compilation.prototype.renderNamedBlock = function(node){
    if(!this.blocklist[node.name]) this.blocklist[node.name] = {};
    this.blocklist[node.name][node.mode||"replace"] = node;
    if(node.mode == "replace" || node.mode === undefined){
        this.block.writeLine("this."+node.name+"(__add);")
    }
}

module.exports = Compilation;
<commit_msg>allow appending/prepending in blocks, fix if else, fix mixins<commit_after>var parse = require("pug-parser");
var lex = require("pug-lexer");
var Block = require("./block.js");
var tags = "a abbr address area article aside audio b base bdi bdo big blockquote body br button canvas caption cite code col colgroup data datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hr html i iframe img input ins kbd keygen label legend li link main map mark menu menuitem meta meter nav noscript object ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strong style sub summary sup table tbody td textarea tfoot th thead time title tr track u ul var video wbr circle defs ellipse g line linearGradient mask path pattern polygon polyline radialGradient rect stop svg text tspan".split(" ");

function Compilation(doc,path){
	doc = doc.replace(/\r\n/g,"\n");
    this.path = path;
    this.tree = parse(lex(doc)).nodes;
    this.main = new Block();
    this.mixins = new Block();
    this.script = new Block();
	this.imports = new Block();
	this.members = new Block();
    this.extendblock = new Block();
    this.blocks = new Block();
    this.block = this.main;
    this.blocklist = null;
}

Compilation.prototype = Object.create(Block.prototype);

Compilation.prototype.compile = function(){
    this.deblock(this.tree);
    this.tree.forEach(function(node){
        if(node.type == "Tag" && node.name == "script") node.name = "main";
    })
	this.block.writeLine('"use strict";');
    this.block.writeLine("var React = require(\"react\");");
    this.block.writeLine("var jade2react = require(\"jade2react\");");
	this.block.writeBlock(this.imports);
    this.block.writeLine("");
    this.block.writeBlock(this.extendblock); //start of class
    this.block.indent(1);
    this.block.writeLine("_render(__add){",1);
    this.renderNodes(this.tree,true);
    if(this.extendblock.out.length){
        this.block.writeLine("super._render.call(this,__add);");
    }
    this.block.indent(-1);
    this.block.writeLine("}");
    this.block.writeLine("render(){",1);
    this.block.writeLine("return jade2react.render(this,this._render)[0];",-1);
    this.block.writeLine("}");
	while(this.blocklist){
		var blocklist = this.blocklist;
		this.blocklist = null;
	    for(var block in blocklist){
	        var b = blocklist[block];
	        this.block.writeLine(block+"(__add){",1);
	        if(b.prepend){
	            this.renderNodes(b.prepend.nodes);
	        }
	        if(b.replace){
	            this.renderNodes(b.replace.nodes);
	        }else{
	            this.block.writeLine("super."+block+".call(this,__add);");
	        }
	        if(b.append){
	            this.renderNodes(b.append.nodes);
	        }
	        this.block.indent(-1);
	        this.block.writeLine("}");
	    }
	}
    if(this.mixins.out.length){
        this.block.writeBlock(this.mixins);
    }
	if(this.members.out.length){
		this.block.writeBlock(this.members);
	}
    this.block.indent(-1);
    this.block.writeLine("}"); //end of class
	this.block.writeLine("module.exports = Component;");
    this.block.writeLine("");
    this.block.writeBlock(this.script);

    if(!this.extendblock.out.length){
        this.renderExtends()
    }

    var code = this.block.build();
    return code;
}

Compilation.prototype.deblock = function(nodes){
    for(var i = 0; i < nodes.length; i++){
        var node = nodes[i];
        if(node.block) node.nodes = node.block.nodes;
        if(node.nodes) this.deblock(node.nodes);
		if(node.consequent) this.deblock(node.consequent.nodes)
		if(node.alternate){
			if(node.alternate.type == "Block"){
				this.deblock(node.alternate.nodes)
			}else{
				this.deblock([node.alternate.consequent,node.alternate.alternate])
			}
		}
    }
}

Compilation.prototype.renderNode = function(node){
    switch(node.type){
        case "Text":
        case "Code":
        case "Tag":
        case "Block":
        case "Each":
        case "Comment":
        case "BlockComment":
        case "Case":
        case "When":
        case "Mixin":
        case "MixinBlock":
        case "Extends":
        case "NamedBlock":
		case "Conditional":
            this["render"+node.type](node);
            break;
    }
}

Compilation.prototype.renderNodes = function(nodes){
    for(var i = 0; i < nodes.length; i++){
        this.renderNode(nodes[i]);
    }
}

Compilation.prototype.renderText = function(node){
    if(!node.val) return;
    this.block.writeLine("__add(\""+node.val+"\");");
}

Compilation.prototype.renderCode = function(node){
    if(!node.val) return;
    if(node.block){
        this.block.writeLine(node.val+"{",1);
        this.renderNodes(node.block.nodes);
        this.block.indent(-1);
        this.block.writeLine("}");
    }else if(node.buffer){
        this.block.writeLine("__add("+node.val+");");
    }else{
        this.block.writeLine(node.val);
    }
}

Compilation.prototype.renderTag = function(node){
    if(node.name=="main"){
		var target = this.script;
		for(var i = 0; i < node.attrs.length; i++){
			if(node.attrs[i].name == "section"){
				switch(node.attrs[i].val){
					case "start":
					case "'start'":
					case '"start"':
						var target = this.imports;
						break;
					case "body":
					case "'body'":
					case '"body"':
						var target = this.members;
						break;
					case "end":
					case "'end'":
					case '"end"':
					default:
						var target = this.script;
						break;
				}
				break;
			}
		}
		var content = [];
        for(var i = 0; i < node.nodes.length; i++){
            switch(node.nodes[i].type){
                case "Text":
                    content.push(node.nodes[i].val);
                    break;
            }
        }
		content.join("").split("\n").forEach(function(line){
			target.writeLine(line);
		});
    }else if(node.name == "children"){
        this.block.writeLine("for(var i = 0; i < this.props.children.length; i++){",1);
        this.block.writeLine("__add(this.props.children[i]);",-1);
        this.block.writeLine("}");
    }else{
        var isDOM = tags.indexOf(node.name)>=0;
        this.block.write("__add(React.createFactory("+(isDOM?"'":"")+node.name+(isDOM?"'":"")+"),");

        if(node.code && node.code.val){
            node.nodes.unshift(node.code)
        }
        if(node.name == "script" || node.name == "style"){
            var script = [];
            for(var i = 0; i < node.nodes.length; i++){
                switch(node.nodes[i].type){
                    case "Code":
                        script.push(node.nodes[i].val);
                        break;
                    case "Text":
                        script.push("\""+node.nodes[i].val.replace(/"/g,"\\\"").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"\"");
                        break;
                }
            }
            script = script.join(" + ");
            if(script){
                node.attrs.push({
                    name:"dangerouslySetInnerHTML",
                    val:"{__html:"+script+"}"
                })
            }
            node.nodes = [];
        }
        this.renderAttributes(node);

        if(node.nodes.length){
            this.block.writeLine(",function(__add){",1);
            this.renderNodes(node.nodes);
            this.block.indent(-1);
            this.block.writeLine("});");
        }else{
            this.block.writeLine(");");
        }
    }
}

Compilation.prototype.renderBlock = function(node){
    this.renderNodes(node.nodes);
}

Compilation.prototype.renderAttributes = function(node){
    if(node.attrs){
        var attributes = {};
        for(var i = 0; i < node.attrs.length; i++){
            var att = node.attrs[i];
            if(attributes[att.name] && att.name == "class"){
                attributes[att.name] += " + \" \" + "+att.val;
            }else{
                attributes[att.name] = att.val;
            }
        }

        if(attributes.class){
            attributes.className = attributes.class;
            delete attributes.class;
        }
        if(node.attributeBlocks.length) this.block.write("jade2react.mixinAttributes(");
        var first = true;
        this.block.write("{");
        for(var att in attributes){
            this.block.write((first?"":",")+'"'+att+'":'+(att=="style"?"jade2react.mapStyle(":"")+attributes[att]+(att=="style"?")":""));
            first = false;
        }
        this.block.write("}");
        if(node.attributeBlocks.length) this.block.write(",["+node.attributeBlocks.join(",")+"])");
    }else{
        this.block.write("{}");
    }
}

Compilation.prototype.renderEach = function(node){
    this.block.writeLine("for(var "+(node.key||"__key")+" in "+node.obj+"){",1);
    if(node.val){
        this.block.writeLine("var "+node.val+" = "+node.obj+"["+(node.key||"__key")+"];");
    }
    this.renderNodes(node.nodes);
    this.block.indent(-1);
    this.block.writeLine("}");
}

Compilation.prototype.renderComment = function(node){
    this.block.writeLine("//"+node.val)
}

Compilation.prototype.renderBlockComment = function(node){
    this.block.writeLine("/*",1);
    for(var i = 0; i < node.nodes.length; i++){
        this.block.writeLine(node.nodes[i].val);
    }
    this.block.indent(-1);
    this.block.writeLine("*/");
}

Compilation.prototype.renderCase = function(node){
    this.block.writeLine("switch("+node.expr+"){",1);
    this.renderNodes(node.nodes);
    this.block.indent(-1);
    this.block.writeLine("}");
}

Compilation.prototype.renderWhen = function(node){
    this.block.writeLine("case "+node.expr+":",1);
    if(node.nodes){
        this.renderNodes(node.nodes);
        this.block.writeLine("break;");
    }
    this.block.indent(-1);
}

Compilation.prototype.renderMixin = function(node){
    if(node.call){
        this.block.write("this."+node.name+"(");
        if(node.nodes){
            this.block.writeLine("jade2react.render(this,function(__add){",1);
            this.renderNodes(node.nodes);
            this.block.indent(-1);
            this.block.write("}),");
        }else{
            this.block.write("[],");
        }
        this.renderAttributes(node);
        this.block.writeLine(","+node.args+").forEach(__add);");
    }else{
        var prev = this.block;
        this.block = new Block();
        this.mixins.writeBlock(this.block);

        if(node.args){
            var argparts = node.args.split("...");
            argparts[0] = argparts[0].trim();
            if(argparts[0][argparts[0].length-1] == ","){
                argparts[0] = argparts[0].slice(0,-1);
            }
            argparts[0] = ","+argparts[0];
        }else{
            argparts = [""];
        }

        this.block.writeLine(node.name+"(block,attributes"+argparts[0]+"){",1);
        if(argparts.length > 1){
            this.block.writeLine("var "+argparts[1].trim()+" = Array.prototype.slice.call(arguments,"+argparts[0].split(",").length+");");
        }
        this.block.writeLine("return jade2react.render(this,function(__add){",1);
        this.renderNodes(node.nodes)
        this.block.indent(-1);
        this.block.writeLine("});");
        this.block.indent(-1);
        this.block.writeLine("}");
        this.block = prev;
    }
}

Compilation.prototype.renderMixinBlock = function(node){
    this.block.writeLine("block.forEach(__add)")
}

Compilation.prototype.renderExtends = function(node){
    var baseComponent = (node?("require(\""+node.file.path+"\")"):"React.Component");
	this.extendblock.writeLine("var Base = "+baseComponent+";");
    this.extendblock.writeLine("class Component extends Base{");
}

Compilation.prototype.renderNamedBlock = function(node){
	if(!this.blocklist) this.blocklist = {};
    if(!this.blocklist[node.name]) this.blocklist[node.name] = {};
    this.blocklist[node.name][node.mode||"replace"] = node;
    if(node.mode == "replace" || node.mode === undefined){
        this.block.writeLine("this."+node.name+"(__add);")
    }
}

Compilation.prototype.renderConditional = function(node){
	this.block.writeLine("if ("+node.test+"){",1);
	this.renderNode(node.consequent);
	this.block.indent(-1);
	if(node.alternate){
		this.block.writeLine("}else{",1);
		this.renderNode(node.alternate);
		this.block.indent(-1);
	}
	this.block.writeLine("}");
}

module.exports = Compilation;
<|endoftext|>


<commit_before>'use strict';
const utils = require('../utils/utils');
const processor = require('./processor');
const tagNameRE = /(end)?(\w+)/;
let mus;

class Ast {
  constructor(html, options = {}, fileUrl) {
    this.root = [];
    this.parent = null;
    this.scanIndex = 0;
    this.endIndex = 0;
    this.template = html;
    this.fileUrl = fileUrl;
    this.blockStart = options.blockStart || '{%';
    this.blockEnd = options.blockEnd || '%}';
    this.variableStart = options.variableStart || '{{';
    this.variableEnd = options.variableEnd || '}}';
    this.processor = processor;
    this.commentStart = '{#';
    this.commentEnd = '#}';

    // support extending processor
    if (options.processor) {
      this.processor = Object.assign({}, processor, options.processor);
    }

    if (this.blockStart === this.variableStart) {
      throw new Error('blockStart should be different with variableStart!');
    }

    // create a regexp used to match leftStart
    this.startRegexp = utils.cache(
      `symbol_${this.blockStart}_${this.variableStart}_${this.commentStart}`,
      () => {
        // make sure can match the longest start string at first
        const str = [this.blockStart, this.variableStart, this.commentStart]
          .sort((a, b) => (a.length > b.length ? -1 : 1))
          .map(item => utils.reStringFormat(item))
          .join('|');
        return new RegExp(str);
      }
    );

    this.parse(html);
  }

  parse(str) {
    if (!str) {
      return;
    }

    let leftStart;
    let isRaw;
    let element;
    let isTag = false;
    let isComment = false;
    const collector = this.genCollector();
    const parent = this.parent;
    const matches = str.match(this.startRegexp);

    if (matches) {
      this.endIndex = matches.index;
      leftStart = matches[0];
      isTag = leftStart === this.blockStart;
      isComment = leftStart === this.commentStart;
    } else {
      this.endIndex = str.length;
    }

    const chars = str.substring(0, this.endIndex);
    if (chars.length) {
      this.collectChars(chars);
    }

    if (!matches) {
      // parse end
      if (parent) {
        utils.warn(`${parent.tag} was not closed`, parent);
      }
      return;
    }

    // get blockEnd or the other
    const rightEnd = isTag ? this.blockEnd : isComment ? this.commentEnd : this.variableEnd;
    str = this.advance(str, this.endIndex);

    // get rightEnd index
    this.endIndex = str.indexOf(rightEnd);
    const expression = str.substring(leftStart.length, this.endIndex);

    if (isComment) {
      // handle comment
      const el = this.genNode(2, leftStart + expression + rightEnd);
      this.processor.comment(el);
      this.collectChars(el);
      this.endIndex = this.endIndex >= 0 ? (this.endIndex + rightEnd.length) : str.length;
    } else if (this.endIndex < 0 || expression.indexOf(leftStart) >= 0) {
      // handle text
      this.collectChars(leftStart);
      this.endIndex = leftStart.length;
    } else {
      // handle block or variable
      this.endIndex = this.endIndex + rightEnd.length;

      isRaw = parent && parent.raw;
      if (isTag) {
        if (!isRaw) {
          const matches = expression.match(tagNameRE);
          const tagName = matches[0];
          const isCustom = mus && mus.customTags.has(tagName);
          const tagHandle = this.processor[tagName]
            || (isCustom ? this.processor.custom : null);

          if (tagHandle) {
            // create ast node
            element = this.genNode(1, expression.substring(matches.index + tagName.length).trim());
            element[tagName] = true;
            element.tag = tagName;
            tagHandle.apply(
              this.processor,
              [element, isCustom ? mus.customTags.get(tagName) : null]
            );

            if (!element.isUnary) {
              this.parent = element;
            }
          } else if (matches[1]) {
            this.closeTag(matches[2]);
          } else {
            utils.throw(`unknown tag '${expression.trim()}'`, this.genNode(null));
          }
        } else if (expression.trim() === 'endraw') {
          this.closeTag('raw');
        } else {
          this.collectChars(leftStart + expression + rightEnd);
        }
      } else {
        if (isRaw) {
          this.collectChars(leftStart + expression + rightEnd);
        } else {
          element = this.genNode(3, expression);
          this.processor.variable(element);
        }
      }

      if (element && !element.isAlone) {
        collector.push(element);
      }
    }

    this.parse(this.advance(str, this.endIndex));
  }

  genNode(type, expr) {
    return type === 2 ? {
      type,
      text: expr,
      _index: this.scanIndex,
    } : {
      type,
      parent: this.parent,
      _expr: expr,
      _index: this.scanIndex,
      _len: this.endIndex,
      _ast: this,
    };
  }

  genMacro() {
    if (!this.macro) {
      this.macro = new Map();
    }

    return this.macro;
  }

  advance(str, index) {
    this.scanIndex += index;
    return str.substring(index);
  }

  genCollector() {
    return this.parent
      ? (this.parent.children = this.parent.children || [])
      : this.root;
  }

  collectChars(str) {
    const collector = this.genCollector();
    const lastEl = collector[collector.length - 1];
    let el;
    if (typeof str === 'string') {
      el = this.genNode(2, str);
      this.processor.text(el);
    } else {
      el = str;
    }
    if (lastEl && lastEl.type === 2) {
      lastEl.text += el.text;
    } else {
      collector.push(el);
    }
  }

  // close block
  // change current parent
  closeTag(tagName) {
    const p = this.parent;
    if (p) {
      this.parent = this.parent.parent;

      if (p.tag !== tagName) {
        if (!p.else && !p.elseif) {
          utils.warn(`${p.tag} was not closed`, p);
        }
        return this.closeTag(tagName);
      } else {
        return p;
      }
    }
  }
}

module.exports = function(html, options, fileUrl, musObj) {
  mus = musObj;
  const ast = new Ast(html, options, fileUrl);
  mus = null;
  return ast;
};
<commit_msg>fix: add elif support<commit_after>'use strict';
const utils = require('../utils/utils');
const processor = require('./processor');
const tagNameRE = /(end)?(\w+)/;
let mus;

class Ast {
  constructor(html, options = {}, fileUrl) {
    this.root = [];
    this.parent = null;
    this.scanIndex = 0;
    this.endIndex = 0;
    this.template = html;
    this.fileUrl = fileUrl;
    this.blockStart = options.blockStart || '{%';
    this.blockEnd = options.blockEnd || '%}';
    this.variableStart = options.variableStart || '{{';
    this.variableEnd = options.variableEnd || '}}';
    this.processor = processor;
    this.commentStart = '{#';
    this.commentEnd = '#}';

    // support extending processor
    if (options.processor) {
      this.processor = Object.assign({}, processor, options.processor);
    }

    if (this.blockStart === this.variableStart) {
      throw new Error('blockStart should be different with variableStart!');
    }

    // create a regexp used to match leftStart
    this.startRegexp = utils.cache(
      `symbol_${this.blockStart}_${this.variableStart}_${this.commentStart}`,
      () => {
        // make sure can match the longest start string at first
        const str = [this.blockStart, this.variableStart, this.commentStart]
          .sort((a, b) => (a.length > b.length ? -1 : 1))
          .map(item => utils.reStringFormat(item))
          .join('|');
        return new RegExp(str);
      }
    );

    this.parse(html);
  }

  parse(str) {
    if (!str) {
      return;
    }

    let leftStart;
    let isRaw;
    let element;
    let isTag = false;
    let isComment = false;
    const collector = this.genCollector();
    const parent = this.parent;
    const matches = str.match(this.startRegexp);

    if (matches) {
      this.endIndex = matches.index;
      leftStart = matches[0];
      isTag = leftStart === this.blockStart;
      isComment = leftStart === this.commentStart;
    } else {
      this.endIndex = str.length;
    }

    const chars = str.substring(0, this.endIndex);
    if (chars.length) {
      this.collectChars(chars);
    }

    if (!matches) {
      // parse end
      if (parent) {
        utils.warn(`${parent.tag} was not closed`, parent);
      }
      return;
    }

    // get blockEnd or the other
    const rightEnd = isTag ? this.blockEnd : isComment ? this.commentEnd : this.variableEnd;
    str = this.advance(str, this.endIndex);

    // get rightEnd index
    this.endIndex = str.indexOf(rightEnd);
    const expression = str.substring(leftStart.length, this.endIndex);

    if (isComment) {
      // handle comment
      const el = this.genNode(2, leftStart + expression + rightEnd);
      this.processor.comment(el);
      this.collectChars(el);
      this.endIndex = this.endIndex >= 0 ? (this.endIndex + rightEnd.length) : str.length;
    } else if (this.endIndex < 0 || expression.indexOf(leftStart) >= 0) {
      // handle text
      this.collectChars(leftStart);
      this.endIndex = leftStart.length;
    } else {
      // handle block or variable
      this.endIndex = this.endIndex + rightEnd.length;

      isRaw = parent && parent.raw;
      if (isTag) {
        if (!isRaw) {
          const matches = expression.match(tagNameRE);
          const tagName = matches[0];
          const isCustom = mus && mus.customTags.has(tagName);
          const tagHandle = this.processor[tagName]
            || (isCustom ? this.processor.custom : null);

          if (tagHandle) {
            // create ast node
            element = this.genNode(1, expression.substring(matches.index + tagName.length).trim());
            element[tagName] = true;
            element.tag = tagName;
            tagHandle.apply(
              this.processor,
              [element, isCustom ? mus.customTags.get(tagName) : null]
            );

            if (!element.isUnary) {
              this.parent = element;
            }
          } else if (matches[1]) {
            this.closeTag(matches[2]);
          } else {
            utils.throw(`unknown tag '${expression.trim()}'`, this.genNode(null));
          }
        } else if (expression.trim() === 'endraw') {
          this.closeTag('raw');
        } else {
          this.collectChars(leftStart + expression + rightEnd);
        }
      } else {
        if (isRaw) {
          this.collectChars(leftStart + expression + rightEnd);
        } else {
          element = this.genNode(3, expression);
          this.processor.variable(element);
        }
      }

      if (element && !element.isAlone) {
        collector.push(element);
      }
    }

    this.parse(this.advance(str, this.endIndex));
  }

  genNode(type, expr) {
    return type === 2 ? {
      type,
      text: expr,
      _index: this.scanIndex,
    } : {
      type,
      parent: this.parent,
      _expr: expr,
      _index: this.scanIndex,
      _len: this.endIndex,
      _ast: this,
    };
  }

  genMacro() {
    if (!this.macro) {
      this.macro = new Map();
    }

    return this.macro;
  }

  advance(str, index) {
    this.scanIndex += index;
    return str.substring(index);
  }

  genCollector() {
    return this.parent
      ? (this.parent.children = this.parent.children || [])
      : this.root;
  }

  collectChars(str) {
    const collector = this.genCollector();
    const lastEl = collector[collector.length - 1];
    let el;
    if (typeof str === 'string') {
      el = this.genNode(2, str);
      this.processor.text(el);
    } else {
      el = str;
    }
    if (lastEl && lastEl.type === 2) {
      lastEl.text += el.text;
    } else {
      collector.push(el);
    }
  }

  // close block
  // change current parent
  closeTag(tagName) {
    const p = this.parent;
    if (p) {
      this.parent = this.parent.parent;

      if (p.tag !== tagName) {
        if (!p.else && !p.elseif && !p.elif) {
          utils.warn(`${p.tag} was not closed`, p);
        }
        return this.closeTag(tagName);
      } else {
        return p;
      }
    }
  }
}

module.exports = function(html, options, fileUrl, musObj) {
  mus = musObj;
  const ast = new Ast(html, options, fileUrl);
  mus = null;
  return ast;
};
<|endoftext|>


<commit_before>var fs = require('fs'),
	path = require('path'),
	mod = require('../../mod.js'),
	util = mod.util,
	notice = mod.notice,
	_conf = mod.conf,
	watchFileCh = mod.load('watchFileCh'),

	ProjConfigData = require('./ProjConfigData.js'),
	_projs = ProjConfigData.projs,
	_alias = ProjConfigData.alias,

	// 新建一个空白项目，用于存放没有项目遮盖的文件
	// 注意：直接使用new 生成的对象，不会在_projs中注册
	defaultProj = new ProjConfigData.ProjConfigClass(_conf.DocumentRoot, '', {}),
	_domainSplit = '.'+_conf.Domain.toLowerCase();

ProjConfigData.defaultProj = defaultProj;
ProjConfigData.find = function(file){
	var conf;
	util.eachObject(_projs, function(i){
		if (!file.indexOf(i)) {
			conf = _projs[i];
			return false;
		}
	});

	return conf;
};
ProjConfigData.DRS = function(host){					// Document Root System  来自于dns
	host = host.toLowerCase().split(_domainSplit);	// 由于端口绑定，所以不用太计较是否真的切分成这样，我们只关注host[0]获取到的值
	// 注意：域名浏览器会自动转化为小写，所以不需要执行toLowerCase方法
	if (host.length) host = _alias[host[0]];
	return host || _conf.DocumentRoot;
};
ProjConfigData.reload = reloadProjConfig;
ProjConfigData.clearCache = clearCache;
ProjConfigData.removeProj = removeProj;

module.exports = ProjConfigData;

// 每xxx分钟，清除缓存一次
setInterval(clearCache, _conf.AutoClearCache);

// 初始化项目属性
fs.readdir(_conf.DocumentRoot, function(err, files) {
	if (err) {
		notice.error('SYS', 'Serv Document not exists', _conf.DocumentRoot);
		return;
	}
	files.forEach(function(v){
		if (v != '.'&& v != '..') {
			var file = _conf.DocumentRoot +v+'/'+_conf.MainConfigFile;
			fs.exists(file, function(exists){
				if (exists) reloadProjConfig(v, true);
			});
		}
	});
});

function removeProj(root){
	if (_projs[root]) {
		util.eachObject(_projs[root].watchFiles, function(i, v){
			v.close();
		});
		delete _projs[root];
	}
}

/* 读取配置文件 */
function reloadProjConfig(dirname, isInit){
	var root = _conf.DocumentRoot+dirname+'/',
		mainFile = root + _conf.MainConfigFile,
		mainPath, mainConf,
		myFile = root + _conf.ProjConfigFile,
		myConf,
		projConf, watchFunc,
		MinCssName;

	if (isInit) {
		watchFunc = function(file){
			watchFileCh(file, function(){
				removeProj(projConf.root);
				if (fs.existsSync(mainFile)) {
					var rs = reloadProjConfig(dirname);
					if (rs) {
						projConf = rs;
						notice.log('ProjConf', 'Updataed Content', file);
					}
					projConf.emit('pageReload');
				} else {
					notice.warn('ProjConf', 'Proj Main Config File has removed', mainFile);
				}
			}, function(e){
				notice.warn('ProjConf', 'config File '+e, file);
			});
		};
	}

	// 读取主配置文件
	try {
		delete require.cache[require.resolve(mainFile)];
		mainConf = require(mainFile);
		if (isInit) watchFunc(mainFile);
		if (mainConf.MinCssName) {
			MinCssName = typeof(MinCssName) == 'object' ? MinCssName : {};
		}

		// 读取个人配置文件
		if (fs.existsSync(myFile)) {
			delete require.cache[require.resolve(myFile)];
			myConf = require(myFile);
			if (isInit) watchFunc(myFile);

			if (myConf.sync) mainConf.sync = myConf.sync;
			if (myConf.alias) mainConf.alias = myConf.alias;
			// if (myConf.dataAPI) mainConf.dataAPI = myConf.dataAPI;
		}

		// 读取附加配置文件
		if (mainConf.extra) {
			mainPath = path.dirname(mainFile)+'/';

			mainConf.extra.map(function(v){
				var file = mainPath+v,
					conf, HTML, fileMap;

				if (!fs.existsSync(file)) {
					notice.warn('ProjConf', 'extra conf file do not exists', file);
					return;
				}

				delete require.cache[require.resolve(file)];
				conf = require(file);
				if (isInit) watchFunc(file);

				// 合并HTML配置
				if (conf.HTML) {
					HTML = mainConf.HTML;
					util.eachObject(conf.HTML, function(file, outConf){
						if (HTML[file]) {
							if (outConf['footer'] || outConf['header']) {
								notice.warn('ProjConf', 'Do not insertCan not override "header" or "footer" in HTML Config', file);
								return;
							}
							util.eachObject(outConf, function(path, data){
								if (HTML[file][path]) {
									notice.warn('ProjConf', 'Can not override "'+path+'" in HTML Config', file);
								} else {
									HTML[file][path] = data;
								}
							});
						} else {
							HTML[file] = outConf;
						}
					});
				}

				// 合并文件映射配置
				if (conf.fileMap) {
					fileMap = mainConf.fileMap;

					util.eachObject(conf.fileMap, function(file, srcArr){
						if (fileMap[file]) {
							fileMap[file] = fileMap[file].concat(srcArr);
						} else {
							fileMap[file] = srcArr;
						}
					});
				}

				// 合并MinCssName
				if (conf.MinCssName) {
					util.eachObject(conf.MinCssName, function(i, v){
						if (MinCssName[i]) notice.warn('ProjConf', 'MinCssName('+i+') has inited');
						MinCssName[i] = v;
					});
				}
			});
		}

		projConf = new ProjConfigData.ProjConfigClass(dirname, mainConf);

		// 注册
		_projs[projConf.root] = projConf;
		if (isInit) notice.log('ProjConf', 'read conf file success', mainFile);

		return projConf;

	} catch(e) {
		notice.warn('ProjConf', e);
		return false;
	}
}

function clearCache(){
	defaultProj.cache = {};
	util.eachObject(_projs, function(i, v){
		v.cache = {};
	});
}

<commit_msg>removeProj时，同时删除alias资源的绑定<commit_after>var fs = require('fs'),
	path = require('path'),
	mod = require('../../mod.js'),
	util = mod.util,
	notice = mod.notice,
	_conf = mod.conf,
	watchFileCh = mod.load('watchFileCh'),

	ProjConfigData = require('./ProjConfigData.js'),
	_projs = ProjConfigData.projs,
	_alias = ProjConfigData.alias,

	// 新建一个空白项目，用于存放没有项目遮盖的文件
	// 注意：直接使用new 生成的对象，不会在_projs中注册
	defaultProj = new ProjConfigData.ProjConfigClass(_conf.DocumentRoot, '', {}),
	_domainSplit = '.'+_conf.Domain.toLowerCase();

ProjConfigData.defaultProj = defaultProj;
ProjConfigData.find = function(file){
	var conf;
	util.eachObject(_projs, function(i){
		if (!file.indexOf(i)) {
			conf = _projs[i];
			return false;
		}
	});

	return conf;
};
ProjConfigData.DRS = function(host){					// Document Root System  来自于dns
	host = host.toLowerCase().split(_domainSplit);	// 由于端口绑定，所以不用太计较是否真的切分成这样，我们只关注host[0]获取到的值
	// 注意：域名浏览器会自动转化为小写，所以不需要执行toLowerCase方法
	if (host.length) host = _alias[host[0]];
	return host || _conf.DocumentRoot;
};
ProjConfigData.reload = reloadProjConfig;
ProjConfigData.clearCache = clearCache;
ProjConfigData.removeProj = removeProj;

module.exports = ProjConfigData;

// 每xxx分钟，清除缓存一次
setInterval(clearCache, _conf.AutoClearCache);

// 初始化项目属性
fs.readdir(_conf.DocumentRoot, function(err, files) {
	if (err) {
		notice.error('SYS', 'Serv Document not exists', _conf.DocumentRoot);
		return;
	}
	files.forEach(function(v){
		if (v != '.'&& v != '..') {
			var file = _conf.DocumentRoot +v+'/'+_conf.MainConfigFile;
			fs.exists(file, function(exists){
				if (exists) reloadProjConfig(v, true);
			});
		}
	});
});

function removeProj(root){
	var projConf = _projs[root];

	if (projConf) {
		util.eachObject(projConf.watchFiles, function(i, v){
			v.close();
		});
		if (projConf.alias) delete _alias[projConf.alias];
		delete _projs[root];
	}
}

/* 读取配置文件 */
function reloadProjConfig(dirname, isInit){
	var root = _conf.DocumentRoot+dirname+'/',
		mainFile = root + _conf.MainConfigFile,
		mainPath, mainConf,
		myFile = root + _conf.ProjConfigFile,
		myConf,
		projConf, watchFunc,
		MinCssName;

	if (isInit) {
		watchFunc = function(file){
			watchFileCh(file, function(){
				removeProj(projConf.root);
				if (fs.existsSync(mainFile)) {
					var rs = reloadProjConfig(dirname);
					if (rs) {
						projConf = rs;
						notice.log('ProjConf', 'Updataed Content', file);
					}
					projConf.emit('pageReload');
				} else {
					notice.warn('ProjConf', 'Proj Main Config File has removed', mainFile);
				}
			}, function(e){
				notice.warn('ProjConf', 'config File '+e, file);
			});
		};
	}

	// 读取主配置文件
	try {
		delete require.cache[require.resolve(mainFile)];
		mainConf = require(mainFile);
		if (isInit) watchFunc(mainFile);
		if (mainConf.MinCssName) {
			MinCssName = typeof(MinCssName) == 'object' ? MinCssName : {};
		}

		// 读取个人配置文件
		if (fs.existsSync(myFile)) {
			delete require.cache[require.resolve(myFile)];
			myConf = require(myFile);
			if (isInit) watchFunc(myFile);

			if (myConf.sync) mainConf.sync = myConf.sync;
			if (myConf.alias) mainConf.alias = myConf.alias;
			// if (myConf.dataAPI) mainConf.dataAPI = myConf.dataAPI;
		}

		// 读取附加配置文件
		if (mainConf.extra) {
			mainPath = path.dirname(mainFile)+'/';

			mainConf.extra.map(function(v){
				var file = mainPath+v,
					conf, HTML, fileMap;

				if (!fs.existsSync(file)) {
					notice.warn('ProjConf', 'extra conf file do not exists', file);
					return;
				}

				delete require.cache[require.resolve(file)];
				conf = require(file);
				if (isInit) watchFunc(file);

				// 合并HTML配置
				if (conf.HTML) {
					HTML = mainConf.HTML;
					util.eachObject(conf.HTML, function(file, outConf){
						if (HTML[file]) {
							if (outConf['footer'] || outConf['header']) {
								notice.warn('ProjConf', 'Do not insertCan not override "header" or "footer" in HTML Config', file);
								return;
							}
							util.eachObject(outConf, function(path, data){
								if (HTML[file][path]) {
									notice.warn('ProjConf', 'Can not override "'+path+'" in HTML Config', file);
								} else {
									HTML[file][path] = data;
								}
							});
						} else {
							HTML[file] = outConf;
						}
					});
				}

				// 合并文件映射配置
				if (conf.fileMap) {
					fileMap = mainConf.fileMap;

					util.eachObject(conf.fileMap, function(file, srcArr){
						if (fileMap[file]) {
							fileMap[file] = fileMap[file].concat(srcArr);
						} else {
							fileMap[file] = srcArr;
						}
					});
				}

				// 合并MinCssName
				if (conf.MinCssName) {
					util.eachObject(conf.MinCssName, function(i, v){
						if (MinCssName[i]) notice.warn('ProjConf', 'MinCssName('+i+') has inited');
						MinCssName[i] = v;
					});
				}
			});
		}

		projConf = new ProjConfigData.ProjConfigClass(dirname, mainConf);

		// 注册
		_projs[projConf.root] = projConf;
		if (isInit) notice.log('ProjConf', 'read conf file success', mainFile);

		return projConf;

	} catch(e) {
		notice.warn('ProjConf', e);
		return false;
	}
}

function clearCache(){
	defaultProj.cache = {};
	util.eachObject(_projs, function(i, v){
		v.cache = {};
	});
}

<|endoftext|>


<commit_before>import lang from 'dojo/_base/lang';
import declare from 'dojo/_base/declare';
import dNumber from 'dojo/number';
import has from 'dojo/has';
import getResource from 'argos/I18n';

const resource = getResource('fileManager');

/**
 * @class crm.FileManager
 *
 */
const __class = declare('crm.FileManager', null, {
  unableToUploadText: resource.unableToUploadText,
  unknownSizeText: resource.unknownSizeText,
  unknownErrorText: resource.unknownErrorText,
  largeFileWarningText: resource.largeFileWarningText,
  largeFileWarningTitle: resource.largeFileWarningTitle,
  percentCompleteText: resource.percentCompleteText,
  fileUploadOptions: {
    maxFileSize: 4000000,
  },
  _store: false,
  _totalProgress: 0,
  _files: null,
  _fileCount: 0,
  _filesUploadedCount: 0,
  _isUploading: false,

  /**
   * @constructor
   */
  constructor: function constructor() {
    this._files = [];
    this.fileUploadOptions.maxFileSize = App.maxUploadFileSize;
  },
  /**
   * Checks if the HTML5 file api is supported.
   * @returns {Boolean}
   */
  isHTML5Supported: function isHTML5Supported() {
    const results = has('html5-file-api');
    return results;
  },
  /**
   * Checks the {@link crm.Application}'s maxFileSize to determine
   * if the file size being added exeeds this limit.
   * @param {Array}
   * @returns {Boolean}
   */
  isFileSizeAllowed: function isFileSizeAllowed(files) {
    let len = 0;
    const maxfileSize = this.fileUploadOptions.maxFileSize;

    for (let i = 0; i < files.length; i++) {
      if (files[i].size === 0) {// eslint-disable-line
        // do nothing.
      } else {
        len += files[i].size || files[i].blob.length;
      }
    }

    if (len > (maxfileSize)) {
      return false;
    }

    return true;
  },
  /**
   * Uploads a file to a URL.
   * @param {File} file
   * @param {String} url
   * @param {Function} progress Progress callback
   * @param {Event} progress.e
   * @param {Function} complete Complete callback
   * @param {Object} complete.request
   * @param {Function} error Error callback
   * @param {Function} error.errorText
   * @param {Object} scope
   * @param {Boolean} asPut
   */
  uploadFile: function uploadFile(file, url, progress, complete, error, scope, asPut) {
    this.uploadFileHTML5(file, url, progress, complete, error, scope, asPut);
  },
  uploadFileHTML5: function uploadFileHTML5(file, url, progress, complete, error, scope, asPut) {
    if (!this.isFileSizeAllowed([file])) {
      this._onUnableToUploadError(this.largeFileWarningText, error);
      return;
    }
    if (this.isHTML5Supported()) {
      this._uploadFileHTML5_asBinary(file, url, progress, complete, error, scope, asPut);
    } else {
      this._onUnableToUploadError(this.unableToUploadText, error);
    }
  },
  _uploadFileHTML5_asBinary: function _uploadFileHTML5_asBinary(file, _url, progress, complete, error, scope, asPut) {// eslint-disable-line
    let url = _url;
    const request = new XMLHttpRequest();
    const service = App.getService();
    window.BlobBuilder = window.BlobBuilder ||
      window.WebKitBlobBuilder ||
      window.MozBlobBuilder ||
      window.MSBlobBuilder;
    if (!url) {
      // assume Attachment SData url
      url = 'slxdata.ashx/slx/system/-/attachments/file'; // TODO: Remove this assumption from SDK
    }

    request.open((asPut) ? 'PUT' : 'POST', url);
    request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

    if (service) {
      request.setRequestHeader('Authorization', service.createBasicAuthToken());
      request.setRequestHeader('X-Authorization', service.createBasicAuthToken());
      request.setRequestHeader('X-Authorization-Mode', 'no-challenge');

      if (App.enableMingle) {
        const accessToken = App.mingleAuthResults.access_token;
        request.setRequestHeader('Authorization', `Bearer ${accessToken}`);
        request.setRequestHeader('X-Authorization', `Bearer ${accessToken}`);
      }
    }

    const reader = new FileReader();
    reader.onload = lang.hitch(this, function readerOnLoad(evt) {
      const unknownErrorText = this.unknownErrorText;
      const blobReader = new FileReader(); // read the blob as an ArrayBuffer to work around this android issue: https://code.google.com/p/android/issues/detail?id=39882
      let bb;
      let usingBlobBuilder;
      let blobData;

      try {
        bb = new Blob(); // This will throw an exception if it is not supported (android)
        bb = [];
      } catch (e) {
        bb = new window.BlobBuilder();
        usingBlobBuilder = true;
      }

      const binary = evt.target.result;
      const boundary = `---------------------------${(new Date()).getTime()}`;
      const dashdash = '--';
      const crlf = '\r\n';

      this._append(bb, dashdash + boundary + crlf);
      this._append(bb, 'Content-Disposition: attachment; ');
      this._append(bb, 'name="file_"; ');
      this._append(bb, `filename*="${encodeURI(file.name)}" `);
      this._append(bb, crlf);
      this._append(bb, `Content-Type: ${file.type}`);
      this._append(bb, crlf + crlf);
      this._append(bb, binary);
      this._append(bb, crlf);
      this._append(bb, dashdash + boundary + dashdash + crlf);

      if (complete) {
        request.onreadystatechange = function onReadyStateChange() {
          if (request.readyState === 4) {
            if (Math.floor(request.status / 100) !== 2) {
              if (error) {
                error.call(scope || this, unknownErrorText);
                console.warn(unknownErrorText + ' ' + request.responseText);// eslint-disable-line
              }
            } else {
              complete.call(scope || this, request);
            }
          }
        };
      }

      if (progress) {
        request.upload.addEventListener('progress', function uploadProgress(e) {
          progress.call(scope || this, e);
        });
      }

      request.setRequestHeader('Content-Type', `multipart/attachment; boundary=${boundary}`);

      if (usingBlobBuilder) {
        blobData = bb.getBlob(file.type);
      } else {
        blobData = new Blob(bb);
      }

      // Read the blob back as an ArrayBuffer to work around this android issue:
      // https://code.google.com/p/android/issues/detail?id=39882
      blobReader.onload = function blobReaderOnLoad(e) {
        request.send(e.target.result);
      };

      blobReader.readAsArrayBuffer(blobData);
    });

    reader.readAsArrayBuffer(file);
  },
  _append: function _append(arrayOrBlobBuilder, data) {
    if (arrayOrBlobBuilder && arrayOrBlobBuilder.constructor === Array) {
      arrayOrBlobBuilder.push(data);
    } else {
      arrayOrBlobBuilder.append(data);
    }
  },
  _onUnableToUploadError: function _onUnableToUploadError(_msg, onError) {
    let msg = _msg;
    if (!msg) {
      msg = this.unableToUploadText;
    }
    if (onError) {
      onError([msg]);
    } else {
      console.warn([msg]); // eslint-disable-line
    }
  },
  /**
   * Formats the file size formatted in KB.
   * @param {Number} size
   * @returns {String}
   */
  formatFileSize: function formatFileSize(_size) {
    let size = _size;
    size = parseInt(size, 10);
    if (size === 0) {
      return '0 KB';
    }
    if (!size || size < 0) {
      return this.unknownSizeText;
    }
    if (size < 1024) {
      return '1 KB';
    }
    return `${dNumber.format(Math.round(size / 1024))} KB`;
  },
  /**
   * Loads a remote file.
   * @param {String} fileUrl
   * @param {String} responseType
   * @param {Function} onSuccess
   * @param {Object} onSuccess.responseInfo
   */
  getFile: function getFile(fileUrl, responseType, onSuccess) {
    const request = new XMLHttpRequest();
    const service = App.getService();
    request.open('GET', fileUrl, true);

    if (responseType) {
      request.responseType = responseType;
    }
    request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

    if (service) {
      request.setRequestHeader('Authorization', service.createBasicAuthToken());
      request.setRequestHeader('X-Authorization', service.createBasicAuthToken());
      request.setRequestHeader('X-Authorization-Mode', 'no-challenge');

      if (App.enableMingle) {
        const accessToken = App.mingleAuthResults.access_token;
        request.setRequestHeader('Authorization', `Bearer ${accessToken}`);
        request.setRequestHeader('X-Authorization', `Bearer ${accessToken}`);
      }
    }

    request.addEventListener('load', function load() {
      const contentType = this.getResponseHeader('Content-Type');
      const contentInfo = this.getResponseHeader('Content-Disposition');
      const fileName = contentInfo.split('=')[1];
      const responseInfo = {
        request: this,
        responseType,
        response: this.response,
        contentType,
        fileName,
      };
      if (onSuccess) {
        onSuccess(responseInfo);
      }
    }, false);
    request.send(null);
  },
});

lang.setObject('Mobile.SalesLogix.FileManager', __class);
export default __class;
<commit_msg>Fix attachment manager<commit_after>import lang from 'dojo/_base/lang';
import declare from 'dojo/_base/declare';
import dNumber from 'dojo/number';
import getResource from 'argos/I18n';

const resource = getResource('fileManager');

/**
 * @class crm.FileManager
 *
 */
const __class = declare('crm.FileManager', null, {
  unableToUploadText: resource.unableToUploadText,
  unknownSizeText: resource.unknownSizeText,
  unknownErrorText: resource.unknownErrorText,
  largeFileWarningText: resource.largeFileWarningText,
  largeFileWarningTitle: resource.largeFileWarningTitle,
  percentCompleteText: resource.percentCompleteText,
  fileUploadOptions: {
    maxFileSize: 4000000,
  },
  _store: false,
  _totalProgress: 0,
  _files: null,
  _fileCount: 0,
  _filesUploadedCount: 0,
  _isUploading: false,

  /**
   * @constructor
   */
  constructor: function constructor() {
    this._files = [];
    this.fileUploadOptions.maxFileSize = App.maxUploadFileSize;
  },
  /**
   * Checks the {@link crm.Application}'s maxFileSize to determine
   * if the file size being added exeeds this limit.
   * @param {Array}
   * @returns {Boolean}
   */
  isFileSizeAllowed: function isFileSizeAllowed(files) {
    let len = 0;
    const maxfileSize = this.fileUploadOptions.maxFileSize;

    for (let i = 0; i < files.length; i++) {
      if (files[i].size === 0) {// eslint-disable-line
        // do nothing.
      } else {
        len += files[i].size || files[i].blob.length;
      }
    }

    if (len > (maxfileSize)) {
      return false;
    }

    return true;
  },
  /**
   * Uploads a file to a URL.
   * @param {File} file
   * @param {String} url
   * @param {Function} progress Progress callback
   * @param {Event} progress.e
   * @param {Function} complete Complete callback
   * @param {Object} complete.request
   * @param {Function} error Error callback
   * @param {Function} error.errorText
   * @param {Object} scope
   * @param {Boolean} asPut
   */
  uploadFile: function uploadFile(file, url, progress, complete, error, scope, asPut) {
    this.uploadFileHTML5(file, url, progress, complete, error, scope, asPut);
  },
  uploadFileHTML5: function uploadFileHTML5(file, url, progress, complete, error, scope, asPut) {
    if (!this.isFileSizeAllowed([file])) {
      this._onUnableToUploadError(this.largeFileWarningText, error);
      return;
    }
    if (App.supportsFileAPI()) {
      this._uploadFileHTML5_asBinary(file, url, progress, complete, error, scope, asPut);
    } else {
      this._onUnableToUploadError(this.unableToUploadText, error);
    }
  },
  _uploadFileHTML5_asBinary: function _uploadFileHTML5_asBinary(file, _url, progress, complete, error, scope, asPut) {// eslint-disable-line
    let url = _url;
    const request = new XMLHttpRequest();
    const service = App.getService();
    window.BlobBuilder = window.BlobBuilder ||
      window.WebKitBlobBuilder ||
      window.MozBlobBuilder ||
      window.MSBlobBuilder;
    if (!url) {
      // assume Attachment SData url
      url = 'slxdata.ashx/slx/system/-/attachments/file'; // TODO: Remove this assumption from SDK
    }

    request.open((asPut) ? 'PUT' : 'POST', url);
    request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

    if (service) {
      request.setRequestHeader('Authorization', service.createBasicAuthToken());
      request.setRequestHeader('X-Authorization', service.createBasicAuthToken());
      request.setRequestHeader('X-Authorization-Mode', 'no-challenge');

      if (App.enableMingle) {
        const accessToken = App.mingleAuthResults.access_token;
        request.setRequestHeader('Authorization', `Bearer ${accessToken}`);
        request.setRequestHeader('X-Authorization', `Bearer ${accessToken}`);
      }
    }

    const reader = new FileReader();
    reader.onload = lang.hitch(this, function readerOnLoad(evt) {
      const unknownErrorText = this.unknownErrorText;
      const blobReader = new FileReader(); // read the blob as an ArrayBuffer to work around this android issue: https://code.google.com/p/android/issues/detail?id=39882
      let bb;
      let usingBlobBuilder;
      let blobData;

      try {
        bb = new Blob(); // This will throw an exception if it is not supported (android)
        bb = [];
      } catch (e) {
        bb = new window.BlobBuilder();
        usingBlobBuilder = true;
      }

      const binary = evt.target.result;
      const boundary = `---------------------------${(new Date()).getTime()}`;
      const dashdash = '--';
      const crlf = '\r\n';

      this._append(bb, dashdash + boundary + crlf);
      this._append(bb, 'Content-Disposition: attachment; ');
      this._append(bb, 'name="file_"; ');
      this._append(bb, `filename*="${encodeURI(file.name)}" `);
      this._append(bb, crlf);
      this._append(bb, `Content-Type: ${file.type}`);
      this._append(bb, crlf + crlf);
      this._append(bb, binary);
      this._append(bb, crlf);
      this._append(bb, dashdash + boundary + dashdash + crlf);

      if (complete) {
        request.onreadystatechange = function onReadyStateChange() {
          if (request.readyState === 4) {
            if (Math.floor(request.status / 100) !== 2) {
              if (error) {
                error.call(scope || this, unknownErrorText);
                console.warn(unknownErrorText + ' ' + request.responseText);// eslint-disable-line
              }
            } else {
              complete.call(scope || this, request);
            }
          }
        };
      }

      if (progress) {
        request.upload.addEventListener('progress', function uploadProgress(e) {
          progress.call(scope || this, e);
        });
      }

      request.setRequestHeader('Content-Type', `multipart/attachment; boundary=${boundary}`);

      if (usingBlobBuilder) {
        blobData = bb.getBlob(file.type);
      } else {
        blobData = new Blob(bb);
      }

      // Read the blob back as an ArrayBuffer to work around this android issue:
      // https://code.google.com/p/android/issues/detail?id=39882
      blobReader.onload = function blobReaderOnLoad(e) {
        request.send(e.target.result);
      };

      blobReader.readAsArrayBuffer(blobData);
    });

    reader.readAsArrayBuffer(file);
  },
  _append: function _append(arrayOrBlobBuilder, data) {
    if (arrayOrBlobBuilder && arrayOrBlobBuilder.constructor === Array) {
      arrayOrBlobBuilder.push(data);
    } else {
      arrayOrBlobBuilder.append(data);
    }
  },
  _onUnableToUploadError: function _onUnableToUploadError(_msg, onError) {
    let msg = _msg;
    if (!msg) {
      msg = this.unableToUploadText;
    }
    if (onError) {
      onError([msg]);
    } else {
      console.warn([msg]); // eslint-disable-line
    }
  },
  /**
   * Formats the file size formatted in KB.
   * @param {Number} size
   * @returns {String}
   */
  formatFileSize: function formatFileSize(_size) {
    let size = _size;
    size = parseInt(size, 10);
    if (size === 0) {
      return '0 KB';
    }
    if (!size || size < 0) {
      return this.unknownSizeText;
    }
    if (size < 1024) {
      return '1 KB';
    }
    return `${dNumber.format(Math.round(size / 1024))} KB`;
  },
  /**
   * Loads a remote file.
   * @param {String} fileUrl
   * @param {String} responseType
   * @param {Function} onSuccess
   * @param {Object} onSuccess.responseInfo
   */
  getFile: function getFile(fileUrl, responseType, onSuccess) {
    const request = new XMLHttpRequest();
    const service = App.getService();
    request.open('GET', fileUrl, true);

    if (responseType) {
      request.responseType = responseType;
    }
    request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

    if (service) {
      request.setRequestHeader('Authorization', service.createBasicAuthToken());
      request.setRequestHeader('X-Authorization', service.createBasicAuthToken());
      request.setRequestHeader('X-Authorization-Mode', 'no-challenge');

      if (App.enableMingle) {
        const accessToken = App.mingleAuthResults.access_token;
        request.setRequestHeader('Authorization', `Bearer ${accessToken}`);
        request.setRequestHeader('X-Authorization', `Bearer ${accessToken}`);
      }
    }

    request.addEventListener('load', function load() {
      const contentType = this.getResponseHeader('Content-Type');
      const contentInfo = this.getResponseHeader('Content-Disposition');
      const fileName = contentInfo.split('=')[1];
      const responseInfo = {
        request: this,
        responseType,
        response: this.response,
        contentType,
        fileName,
      };
      if (onSuccess) {
        onSuccess(responseInfo);
      }
    }, false);
    request.send(null);
  },
});

lang.setObject('Mobile.SalesLogix.FileManager', __class);
export default __class;
<|endoftext|>