diff --git a/README.md b/README.md index 60a1ccf..a2f88f8 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,16 @@ Check [this page](http://sciter.com/developers/sciter-sdk-bindings/) for other l ---- +If you want to use this source + +go.mod +``` +module yourxxx + +require github.com/sciter-sdk/go-sciter latest + +replace github.com/sciter-sdk/go-sciter latest => github.com/wj008/go-sciter latest +``` # Attention diff --git a/callbacks.c b/callbacks.c index 28aad0d..77d7e25 100644 --- a/callbacks.c +++ b/callbacks.c @@ -3,7 +3,7 @@ // typedef BOOL SC_CALLBACK SciterElementCallback(HELEMENT he, LPVOID param); -BOOL SC_CALLBACK SciterElementCallback_cgo(HELEMENT he, LPVOID param) +SBOOL SC_CALLBACK SciterElementCallback_cgo(HELEMENT he, LPVOID param) { return goSciterElementCallback(he, param); } @@ -27,7 +27,7 @@ VOID SC_CALLBACK LPCSTR_RECEIVER_cgo(LPCSTR str, UINT str_length, LPVOID param) } // typedef BOOL SC_CALLBACK ElementEventProc(LPVOID tag, HELEMENT he, UINT evtg, LPVOID prms); -BOOL SC_CALLBACK ElementEventProc_cgo(LPVOID tag, HELEMENT he, UINT evtg, LPVOID prms) +SBOOL SC_CALLBACK ElementEventProc_cgo(LPVOID tag, HELEMENT he, UINT evtg, LPVOID prms) { return goElementEventProc(tag, he, evtg, prms); } @@ -59,7 +59,7 @@ INT SC_CALLBACK ELEMENT_COMPARATOR_cgo(HELEMENT he1, HELEMENT he2, LPVOID param) // typedef BOOL SC_CALLBACK KeyValueCallback(LPVOID param, const VALUE* pkey, const VALUE* pval); -BOOL SC_CALLBACK KeyValueCallback_cgo(LPVOID param, const VALUE* pkey, const VALUE* pval) +SBOOL SC_CALLBACK KeyValueCallback_cgo(LPVOID param, const VALUE* pkey, const VALUE* pval) { return goKeyValueCallback(param, (VALUE*)pkey, (VALUE*)pval); } \ No newline at end of file diff --git a/examples/callback/index.html b/examples/callback/index.html index 0285fd4..8bac59f 100644 --- a/examples/callback/index.html +++ b/examples/callback/index.html @@ -1,13 +1,13 @@
- + \ No newline at end of file diff --git a/examples/resx/html/css/main.css b/examples/resx/html/css/main.css new file mode 100644 index 0000000..74d8003 --- /dev/null +++ b/examples/resx/html/css/main.css @@ -0,0 +1,5 @@ +html { + width: 300px; /* preferred/initial width */ + height: 240px; /* content will not overflow, no vertical scrollbars on the window */ + background-color: #E5B783; +} \ No newline at end of file diff --git a/examples/resx/html/icon/i32.png b/examples/resx/html/icon/i32.png new file mode 100644 index 0000000..25e20ba Binary files /dev/null and b/examples/resx/html/icon/i32.png differ diff --git a/examples/resx/html/index.html b/examples/resx/html/index.html new file mode 100644 index 0000000..b6ef3af --- /dev/null +++ b/examples/resx/html/index.html @@ -0,0 +1,36 @@ + + + + + + Title + + + + + +
+
+ + +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/examples/resx/html/js/util.js b/examples/resx/html/js/util.js new file mode 100644 index 0000000..4e183d2 --- /dev/null +++ b/examples/resx/html/js/util.js @@ -0,0 +1,52 @@ +window.util = {}; + +//open a dialog +util.openDialog = function (url, width = 800, height = 600, data = null) { + return Window.this.modal({ + url: url, + parent: Window.this, + type: Window.FRAME_WINDOW, + width: width, + height: height, + alignment: -8, + parameters: data + }); +} + +//random +util.randomString = function (len) { + len = len || 32; + let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; + let maxPos = chars.length; + let pwd = ''; + for (let i = 0; i < len; i++) { + pwd += chars.charAt(Math.floor(Math.random() * maxPos)); + } + return pwd; +} + +//Center the current window +util.moveCenter = function (width = 0, height = 0) { + let view = Window.this; + let [width1, height1] = view.box('dimension', 'border'); + let [width2, height2] = view.box('dimension', 'client'); + if (width == 0) { + width = width2; + } + if (height == 0) { + height = height2; + } + let nw = width + (width1 - width2); + let nh = height + (height1 - height2); + let [sw, sh] = view.screenBox('frame', 'dimension'); + let left = (sw - nw) / 2; + let top = (sh - nh) / 2; + view.move(left, top, width, height, "client"); +} +//wait ready +util.ready = function (fn) { + let timer = setInterval(function () { + clearInterval(timer) + fn(Window.this) + }, 10) +} \ No newline at end of file diff --git a/examples/resx/html/js/zepto.js b/examples/resx/html/js/zepto.js new file mode 100644 index 0000000..99fe1d6 --- /dev/null +++ b/examples/resx/html/js/zepto.js @@ -0,0 +1,1650 @@ +/* Zepto v1.2.0 - zepto event ajax form ie - zeptojs.com/license */ +(function(global, factory) { + if (typeof define === 'function' && define.amd) + define(function() { return factory(global) }) + else + factory(global) +}(this, function(window) { + var Zepto = (function() { + var undefined, key, $, classList, emptyArray = [], concat = emptyArray.concat, filter = emptyArray.filter, slice = emptyArray.slice, + document = window.document, + elementDisplay = {}, classCache = {}, + cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 }, + fragmentRE = /^\s*<(\w+|!)[^>]*>/, + singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rootNodeRE = /^(?:body|html)$/i, + capitalRE = /([A-Z])/g, + + // special attributes that should be get/set via method calls + methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'], + + adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ], + table = document.createElement('table'), + tableRow = document.createElement('tr'), + containers = { + 'tr': document.createElement('tbody'), + 'tbody': table, 'thead': table, 'tfoot': table, + 'td': tableRow, 'th': tableRow, + '*': document.createElement('div') + }, + readyRE = /complete|loaded|interactive/, + simpleSelectorRE = /^[\w-]*$/, + class2type = {}, + toString = class2type.toString, + zepto = {}, + camelize, uniq, + tempParent = document.createElement('div'), + propMap = { + 'tabindex': 'tabIndex', + 'readonly': 'readOnly', + 'for': 'htmlFor', + 'class': 'className', + 'maxlength': 'maxLength', + 'cellspacing': 'cellSpacing', + 'cellpadding': 'cellPadding', + 'rowspan': 'rowSpan', + 'colspan': 'colSpan', + 'usemap': 'useMap', + 'frameborder': 'frameBorder', + 'contenteditable': 'contentEditable' + }, + isArray = Array.isArray || + function(object){ return object instanceof Array } + + zepto.matches = function(element, selector) { + if (!selector || !element || element.nodeType !== 1) return false + var matchesSelector = element.matches || element.webkitMatchesSelector || + element.mozMatchesSelector || element.oMatchesSelector || + element.matchesSelector + if (matchesSelector) return matchesSelector.call(element, selector) + // fall back to performing a selector: + var match, parent = element.parentNode, temp = !parent + if (temp) (parent = tempParent).appendChild(element) + match = ~zepto.qsa(parent, selector).indexOf(element) + temp && tempParent.removeChild(element) + return match + } + + function type(obj) { + return obj == null ? String(obj) : + class2type[toString.call(obj)] || "object" + } + + function isFunction(value) { return type(value) == "function" } + function isWindow(obj) { return obj != null && obj == obj.window } + function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE } + function isObject(obj) { return type(obj) == "object" } + function isPlainObject(obj) { + return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype + } + + function likeArray(obj) { + var length = !!obj && 'length' in obj && obj.length, + type = $.type(obj) + + return 'function' != type && !isWindow(obj) && ( + 'array' == type || length === 0 || + (typeof length == 'number' && length > 0 && (length - 1) in obj) + ) + } + + function compact(array) { return filter.call(array, function(item){ return item != null }) } + function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array } + camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) } + function dasherize(str) { + return str.replace(/::/g, '/') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') + .replace(/([a-z\d])([A-Z])/g, '$1_$2') + .replace(/_/g, '-') + .toLowerCase() + } + uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) } + + function classRE(name) { + return name in classCache ? + classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')) + } + + function maybeAddPx(name, value) { + return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value + } + + function defaultDisplay(nodeName) { + var element, display + if (!elementDisplay[nodeName]) { + element = document.createElement(nodeName) + document.body.appendChild(element) + display = getComputedStyle(element, '').getPropertyValue("display") + element.parentNode.removeChild(element) + display == "none" && (display = "block") + elementDisplay[nodeName] = display + } + return elementDisplay[nodeName] + } + + function children(element) { + return 'children' in element ? + slice.call(element.children) : + $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node }) + } + + function Z(dom, selector) { + var i, len = dom ? dom.length : 0 + for (i = 0; i < len; i++) this[i] = dom[i] + this.length = len + this.selector = selector || '' + } + + // `$.zepto.fragment` takes a html string and an optional tag name + // to generate DOM nodes from the given html string. + // The generated DOM nodes are returned as an array. + // This function can be overridden in plugins for example to make + // it compatible with browsers that don't support the DOM fully. + zepto.fragment = function(html, name, properties) { + var dom, nodes, container + + // A special case optimization for a single tag + if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1)) + + if (!dom) { + if (html.replace) html = html.replace(tagExpanderRE, "<$1>") + if (name === undefined) name = fragmentRE.test(html) && RegExp.$1 + if (!(name in containers)) name = '*' + + container = containers[name] + container.innerHTML = '' + html + dom = $.each(slice.call(container.childNodes), function(){ + container.removeChild(this) + }) + } + + if (isPlainObject(properties)) { + nodes = $(dom) + $.each(properties, function(key, value) { + if (methodAttributes.indexOf(key) > -1) nodes[key](value) + else nodes.attr(key, value) + }) + } + + return dom + } + + // `$.zepto.Z` swaps out the prototype of the given `dom` array + // of nodes with `$.fn` and thus supplying all the Zepto functions + // to the array. This method can be overridden in plugins. + zepto.Z = function(dom, selector) { + return new Z(dom, selector) + } + + // `$.zepto.isZ` should return `true` if the given object is a Zepto + // collection. This method can be overridden in plugins. + zepto.isZ = function(object) { + return object instanceof zepto.Z + } + + // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and + // takes a CSS selector and an optional context (and handles various + // special cases). + // This method can be overridden in plugins. + zepto.init = function(selector, context) { + var dom + // If nothing given, return an empty Zepto collection + if (!selector) return zepto.Z() + // Optimize for string selectors + else if (typeof selector == 'string') { + selector = selector.trim() + // If it's a html fragment, create nodes from it + // Note: In both Chrome 21 and Firefox 15, DOM error 12 + // is thrown if the fragment doesn't begin with < + if (selector[0] == '<' && fragmentRE.test(selector)) + dom = zepto.fragment(selector, RegExp.$1, context), selector = null + // If there's a context, create a collection on that context first, and select + // nodes from there + else if (context !== undefined) return $(context).find(selector) + // If it's a CSS selector, use it to select nodes. + else dom = zepto.qsa(document, selector) + } + // If a function is given, call it when the DOM is ready + else if (isFunction(selector)) return $(document).ready(selector) + // If a Zepto collection is given, just return it + else if (zepto.isZ(selector)) return selector + else { + // normalize array if an array of nodes is given + if (isArray(selector)) dom = compact(selector) + // Wrap DOM nodes. + else if (isObject(selector)) + dom = [selector], selector = null + // If it's a html fragment, create nodes from it + else if (fragmentRE.test(selector)) + dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null + // If there's a context, create a collection on that context first, and select + // nodes from there + else if (context !== undefined) return $(context).find(selector) + // And last but no least, if it's a CSS selector, use it to select nodes. + else dom = zepto.qsa(document, selector) + } + // create a new Zepto collection from the nodes found + return zepto.Z(dom, selector) + } + + // `$` will be the base `Zepto` object. When calling this + // function just call `$.zepto.init, which makes the implementation + // details of selecting nodes and creating Zepto collections + // patchable in plugins. + $ = function(selector, context){ + return zepto.init(selector, context) + } + + function extend(target, source, deep) { + for (key in source) + if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { + if (isPlainObject(source[key]) && !isPlainObject(target[key])) + target[key] = {} + if (isArray(source[key]) && !isArray(target[key])) + target[key] = [] + extend(target[key], source[key], deep) + } + else if (source[key] !== undefined) target[key] = source[key] + } + + // Copy all but undefined properties from one or more + // objects to the `target` object. + $.extend = function(target){ + var deep, args = slice.call(arguments, 1) + if (typeof target == 'boolean') { + deep = target + target = args.shift() + } + args.forEach(function(arg){ extend(target, arg, deep) }) + return target + } + + // `$.zepto.qsa` is Zepto's CSS selector implementation which + // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`. + // This method can be overridden in plugins. + zepto.qsa = function(element, selector){ + var found, + maybeID = selector[0] == '#', + maybeClass = !maybeID && selector[0] == '.', + nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // Ensure that a 1 char tag name still gets checked + isSimple = simpleSelectorRE.test(nameOnly) + return (element.getElementById && isSimple && maybeID) ? // Safari DocumentFragment doesn't have getElementById + ( (found = element.getElementById(nameOnly)) ? [found] : [] ) : + (element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11) ? [] : + slice.call( + isSimple && !maybeID && element.getElementsByClassName ? // DocumentFragment doesn't have getElementsByClassName/TagName + maybeClass ? element.getElementsByClassName(nameOnly) : // If it's simple, it could be a class + element.getElementsByTagName(selector) : // Or a tag + element.querySelectorAll(selector) // Or it's not simple, and we need to query all + ) + } + + function filtered(nodes, selector) { + return selector == null ? $(nodes) : $(nodes).filter(selector) + } + + $.contains = document.documentElement.contains ? + function(parent, node) { + return parent !== node && parent.contains(node) + } : + function(parent, node) { + while (node && (node = node.parentNode)) + if (node === parent) return true + return false + } + + function funcArg(context, arg, idx, payload) { + return isFunction(arg) ? arg.call(context, idx, payload) : arg + } + + function setAttribute(node, name, value) { + value == null ? node.removeAttribute(name) : node.setAttribute(name, value) + } + + // access className property while respecting SVGAnimatedString + function className(node, value){ + var klass = node.className || '', + svg = klass && klass.baseVal !== undefined + + if (value === undefined) return svg ? klass.baseVal : klass + svg ? (klass.baseVal = value) : (node.className = value) + } + + // "true" => true + // "false" => false + // "null" => null + // "42" => 42 + // "42.5" => 42.5 + // "08" => "08" + // JSON => parse if valid + // String => self + function deserializeValue(value) { + try { + return value ? + value == "true" || + ( value == "false" ? false : + value == "null" ? null : + +value + "" == value ? +value : + /^[\[\{]/.test(value) ? $.parseJSON(value) : + value ) + : value + } catch(e) { + return value + } + } + + $.type = type + $.isFunction = isFunction + $.isWindow = isWindow + $.isArray = isArray + $.isPlainObject = isPlainObject + + $.isEmptyObject = function(obj) { + var name + for (name in obj) return false + return true + } + + $.isNumeric = function(val) { + var num = Number(val), type = typeof val + return val != null && type != 'boolean' && + (type != 'string' || val.length) && + !isNaN(num) && isFinite(num) || false + } + + $.inArray = function(elem, array, i){ + return emptyArray.indexOf.call(array, elem, i) + } + + $.camelCase = camelize + $.trim = function(str) { + return str == null ? "" : String.prototype.trim.call(str) + } + + // plugin compatibility + $.uuid = 0 + $.support = { } + $.expr = { } + $.noop = function() {} + + $.map = function(elements, callback){ + var value, values = [], i, key + if (likeArray(elements)) + for (i = 0; i < elements.length; i++) { + value = callback(elements[i], i) + if (value != null) values.push(value) + } + else + for (key in elements) { + value = callback(elements[key], key) + if (value != null) values.push(value) + } + return flatten(values) + } + + $.each = function(elements, callback){ + var i, key + if (likeArray(elements)) { + for (i = 0; i < elements.length; i++) + if (callback.call(elements[i], i, elements[i]) === false) return elements + } else { + for (key in elements) + if (callback.call(elements[key], key, elements[key]) === false) return elements + } + + return elements + } + + $.grep = function(elements, callback){ + return filter.call(elements, callback) + } + + if (window.JSON) $.parseJSON = JSON.parse + + // Populate the class2type map + $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase() + }) + + // Define methods that will be available on all + // Zepto collections + $.fn = { + constructor: zepto.Z, + length: 0, + + // Because a collection acts like an array + // copy over these useful array functions. + forEach: emptyArray.forEach, + reduce: emptyArray.reduce, + push: emptyArray.push, + sort: emptyArray.sort, + splice: emptyArray.splice, + indexOf: emptyArray.indexOf, + concat: function(){ + var i, value, args = [] + for (i = 0; i < arguments.length; i++) { + value = arguments[i] + args[i] = zepto.isZ(value) ? value.toArray() : value + } + return concat.apply(zepto.isZ(this) ? this.toArray() : this, args) + }, + + // `map` and `slice` in the jQuery API work differently + // from their array counterparts + map: function(fn){ + return $($.map(this, function(el, i){ return fn.call(el, i, el) })) + }, + slice: function(){ + return $(slice.apply(this, arguments)) + }, + + ready: function(callback){ + // need to check if document.body exists for IE as that browser reports + // document ready when it hasn't yet created the body element + if (readyRE.test(document.readyState) && document.body) callback($) + else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false) + return this + }, + get: function(idx){ + return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length] + }, + toArray: function(){ return this.get() }, + size: function(){ + return this.length + }, + remove: function(){ + return this.each(function(){ + if (this.parentNode != null) + this.parentNode.removeChild(this) + }) + }, + each: function(callback){ + emptyArray.every.call(this, function(el, idx){ + return callback.call(el, idx, el) !== false + }) + return this + }, + filter: function(selector){ + if (isFunction(selector)) return this.not(this.not(selector)) + return $(filter.call(this, function(element){ + return zepto.matches(element, selector) + })) + }, + add: function(selector,context){ + return $(uniq(this.concat($(selector,context)))) + }, + is: function(selector){ + return this.length > 0 && zepto.matches(this[0], selector) + }, + not: function(selector){ + var nodes=[] + if (isFunction(selector) && selector.call !== undefined) + this.each(function(idx){ + if (!selector.call(this,idx)) nodes.push(this) + }) + else { + var excludes = typeof selector == 'string' ? this.filter(selector) : + (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector) + this.forEach(function(el){ + if (excludes.indexOf(el) < 0) nodes.push(el) + }) + } + return $(nodes) + }, + has: function(selector){ + return this.filter(function(){ + return isObject(selector) ? + $.contains(this, selector) : + $(this).find(selector).size() + }) + }, + eq: function(idx){ + return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1) + }, + first: function(){ + var el = this[0] + return el && !isObject(el) ? el : $(el) + }, + last: function(){ + var el = this[this.length - 1] + return el && !isObject(el) ? el : $(el) + }, + find: function(selector){ + var result, $this = this + if (!selector) result = $() + else if (typeof selector == 'object') + result = $(selector).filter(function(){ + var node = this + return emptyArray.some.call($this, function(parent){ + return $.contains(parent, node) + }) + }) + else if (this.length == 1) result = $(zepto.qsa(this[0], selector)) + else result = this.map(function(){ return zepto.qsa(this, selector) }) + return result + }, + closest: function(selector, context){ + var nodes = [], collection = typeof selector == 'object' && $(selector) + this.each(function(_, node){ + while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector))) + node = node !== context && !isDocument(node) && node.parentNode + if (node && nodes.indexOf(node) < 0) nodes.push(node) + }) + return $(nodes) + }, + parents: function(selector){ + var ancestors = [], nodes = this + while (nodes.length > 0) + nodes = $.map(nodes, function(node){ + if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) { + ancestors.push(node) + return node + } + }) + return filtered(ancestors, selector) + }, + parent: function(selector){ + return filtered(uniq(this.pluck('parentNode')), selector) + }, + children: function(selector){ + return filtered(this.map(function(){ return children(this) }), selector) + }, + contents: function() { + return this.map(function() { return this.contentDocument || slice.call(this.childNodes) }) + }, + siblings: function(selector){ + return filtered(this.map(function(i, el){ + return filter.call(children(el.parentNode), function(child){ return child!==el }) + }), selector) + }, + empty: function(){ + return this.each(function(){ this.innerHTML = '' }) + }, + // `pluck` is borrowed from Prototype.js + pluck: function(property){ + return $.map(this, function(el){ return el[property] }) + }, + show: function(){ + return this.each(function(){ + this.style.display == "none" && (this.style.display = '') + if (getComputedStyle(this, '').getPropertyValue("display") == "none") + this.style.display = defaultDisplay(this.nodeName) + }) + }, + replaceWith: function(newContent){ + return this.before(newContent).remove() + }, + wrap: function(structure){ + var func = isFunction(structure) + if (this[0] && !func) + var dom = $(structure).get(0), + clone = dom.parentNode || this.length > 1 + + return this.each(function(index){ + $(this).wrapAll( + func ? structure.call(this, index) : + clone ? dom.cloneNode(true) : dom + ) + }) + }, + wrapAll: function(structure){ + if (this[0]) { + $(this[0]).before(structure = $(structure)) + var children + // drill down to the inmost element + while ((children = structure.children()).length) structure = children.first() + $(structure).append(this) + } + return this + }, + wrapInner: function(structure){ + var func = isFunction(structure) + return this.each(function(index){ + var self = $(this), contents = self.contents(), + dom = func ? structure.call(this, index) : structure + contents.length ? contents.wrapAll(dom) : self.append(dom) + }) + }, + unwrap: function(){ + this.parent().each(function(){ + $(this).replaceWith($(this).children()) + }) + return this + }, + clone: function(){ + return this.map(function(){ return this.cloneNode(true) }) + }, + hide: function(){ + return this.css("display", "none") + }, + toggle: function(setting){ + return this.each(function(){ + var el = $(this) + ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide() + }) + }, + prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') }, + next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') }, + html: function(html){ + return 0 in arguments ? + this.each(function(idx){ + var originHtml = this.innerHTML + $(this).empty().append( funcArg(this, html, idx, originHtml) ) + }) : + (0 in this ? this[0].innerHTML : null) + }, + text: function(text){ + return 0 in arguments ? + this.each(function(idx){ + var newText = funcArg(this, text, idx, this.textContent) + this.textContent = newText == null ? '' : ''+newText + }) : + (0 in this ? this.pluck('textContent').join("") : null) + }, + attr: function(name, value){ + var result + return (typeof name == 'string' && !(1 in arguments)) ? + (0 in this && this[0].nodeType == 1 && (result = this[0].getAttribute(name)) != null ? result : undefined) : + this.each(function(idx){ + if (this.nodeType !== 1) return + if (isObject(name)) for (key in name) setAttribute(this, key, name[key]) + else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))) + }) + }, + removeAttr: function(name){ + return this.each(function(){ this.nodeType === 1 && name.split(' ').forEach(function(attribute){ + setAttribute(this, attribute) + }, this)}) + }, + prop: function(name, value){ + name = propMap[name] || name + return (1 in arguments) ? + this.each(function(idx){ + this[name] = funcArg(this, value, idx, this[name]) + }) : + (this[0] && this[0][name]) + }, + removeProp: function(name){ + name = propMap[name] || name + return this.each(function(){ delete this[name] }) + }, + data: function(name, value){ + var attrName = 'data-' + name.replace(capitalRE, '-$1').toLowerCase() + + var data = (1 in arguments) ? + this.attr(attrName, value) : + this.attr(attrName) + + return data !== null ? deserializeValue(data) : undefined + }, + val: function(value){ + if (0 in arguments) { + if (value == null) value = "" + return this.each(function(idx){ + this.value = funcArg(this, value, idx, this.value) + }) + } else { + return this[0] && (this[0].multiple ? + $(this[0]).find('option').filter(function(){ return this.selected }).pluck('value') : + this[0].value) + } + }, + offset: function(coordinates){ + if (coordinates) return this.each(function(index){ + var $this = $(this), + coords = funcArg(this, coordinates, index, $this.offset()), + parentOffset = $this.offsetParent().offset(), + props = { + top: coords.top - parentOffset.top, + left: coords.left - parentOffset.left + } + + if ($this.css('position') == 'static') props['position'] = 'relative' + $this.css(props) + }) + if (!this.length) return null + if (document.documentElement !== this[0] && !$.contains(document.documentElement, this[0])) + return {top: 0, left: 0} + var obj = this[0].getBoundingClientRect() + return { + left: obj.left + window.pageXOffset, + top: obj.top + window.pageYOffset, + width: Math.round(obj.width), + height: Math.round(obj.height) + } + }, + css: function(property, value){ + if (arguments.length < 2) { + var element = this[0] + if (typeof property == 'string') { + if (!element) return + return element.style[camelize(property)] || getComputedStyle(element, '').getPropertyValue(property) + } else if (isArray(property)) { + if (!element) return + var props = {} + var computedStyle = getComputedStyle(element, '') + $.each(property, function(_, prop){ + props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop)) + }) + return props + } + } + + var css = '' + if (type(property) == 'string') { + if (!value && value !== 0) + this.each(function(){ this.style.removeProperty(dasherize(property)) }) + else + css = dasherize(property) + ":" + maybeAddPx(property, value) + } else { + for (key in property) + if (!property[key] && property[key] !== 0) + this.each(function(){ this.style.removeProperty(dasherize(key)) }) + else + css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';' + } + + return this.each(function(){ this.style.cssText += ';' + css }) + }, + index: function(element){ + return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]) + }, + hasClass: function(name){ + if (!name) return false + return emptyArray.some.call(this, function(el){ + return this.test(className(el)) + }, classRE(name)) + }, + addClass: function(name){ + if (!name) return this + return this.each(function(idx){ + if (!('className' in this)) return + classList = [] + var cls = className(this), newName = funcArg(this, name, idx, cls) + newName.split(/\s+/g).forEach(function(klass){ + if (!$(this).hasClass(klass)) classList.push(klass) + }, this) + classList.length && className(this, cls + (cls ? " " : "") + classList.join(" ")) + }) + }, + removeClass: function(name){ + return this.each(function(idx){ + if (!('className' in this)) return + if (name === undefined) return className(this, '') + classList = className(this) + funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){ + classList = classList.replace(classRE(klass), " ") + }) + className(this, classList.trim()) + }) + }, + toggleClass: function(name, when){ + if (!name) return this + return this.each(function(idx){ + var $this = $(this), names = funcArg(this, name, idx, className(this)) + names.split(/\s+/g).forEach(function(klass){ + (when === undefined ? !$this.hasClass(klass) : when) ? + $this.addClass(klass) : $this.removeClass(klass) + }) + }) + }, + scrollTop: function(value){ + if (!this.length) return + var hasScrollTop = 'scrollTop' in this[0] + if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset + return this.each(hasScrollTop ? + function(){ this.scrollTop = value } : + function(){ this.scrollTo(this.scrollX, value) }) + }, + scrollLeft: function(value){ + if (!this.length) return + var hasScrollLeft = 'scrollLeft' in this[0] + if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset + return this.each(hasScrollLeft ? + function(){ this.scrollLeft = value } : + function(){ this.scrollTo(value, this.scrollY) }) + }, + position: function() { + if (!this.length) return + + var elem = this[0], + // Get *real* offsetParent + offsetParent = this.offsetParent(), + // Get correct offsets + offset = this.offset(), + parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset() + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( $(elem).css('margin-top') ) || 0 + offset.left -= parseFloat( $(elem).css('margin-left') ) || 0 + + // Add offsetParent borders + parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0 + parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0 + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + } + }, + offsetParent: function() { + return this.map(function(){ + var parent = this.offsetParent || document.body + while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static") + parent = parent.offsetParent + return parent + }) + } + } + + // for now + $.fn.detach = $.fn.remove + + // Generate the `width` and `height` functions + ;['width', 'height'].forEach(function(dimension){ + var dimensionProperty = + dimension.replace(/./, function(m){ return m[0].toUpperCase() }) + + $.fn[dimension] = function(value){ + var offset, el = this[0] + if (value === undefined) return isWindow(el) ? el['inner' + dimensionProperty] : + isDocument(el) ? el.documentElement['scroll' + dimensionProperty] : + (offset = this.offset()) && offset[dimension] + else return this.each(function(idx){ + el = $(this) + el.css(dimension, funcArg(this, value, idx, el[dimension]())) + }) + } + }) + + function traverseNode(node, fun) { + fun(node) + for (var i = 0, len = node.childNodes.length; i < len; i++) + traverseNode(node.childNodes[i], fun) + } + + // Generate the `after`, `prepend`, `before`, `append`, + // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods. + adjacencyOperators.forEach(function(operator, operatorIndex) { + var inside = operatorIndex % 2 //=> prepend, append + + $.fn[operator] = function(){ + // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings + var argType, nodes = $.map(arguments, function(arg) { + var arr = [] + argType = type(arg) + if (argType == "array") { + arg.forEach(function(el) { + if (el.nodeType !== undefined) return arr.push(el) + else if ($.zepto.isZ(el)) return arr = arr.concat(el.get()) + arr = arr.concat(zepto.fragment(el)) + }) + return arr + } + return argType == "object" || arg == null ? + arg : zepto.fragment(arg) + }), + parent, copyByClone = this.length > 1 + if (nodes.length < 1) return this + + return this.each(function(_, target){ + parent = inside ? target : target.parentNode + + // convert all methods to a "before" operation + target = operatorIndex == 0 ? target.nextSibling : + operatorIndex == 1 ? target.firstChild : + operatorIndex == 2 ? target : + null + + var parentInDocument = $.contains(document.documentElement, parent) + + nodes.forEach(function(node){ + if (copyByClone) node = node.cloneNode(true) + else if (!parent) return $(node).remove() + + parent.insertBefore(node, target) + if (parentInDocument) traverseNode(node, function(el){ + if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' && + (!el.type || el.type === 'text/javascript') && !el.src){ + var target = el.ownerDocument ? el.ownerDocument.defaultView : window + target['eval'].call(target, el.innerHTML) + } + }) + }) + }) + } + + // after => insertAfter + // prepend => prependTo + // before => insertBefore + // append => appendTo + $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){ + $(html)[operator](this) + return this + } + }) + + zepto.Z.prototype = Z.prototype = $.fn + + // Export internal API functions in the `$.zepto` namespace + zepto.uniq = uniq + zepto.deserializeValue = deserializeValue + $.zepto = zepto + + return $ +})() + +window.Zepto = Zepto +window.$ === undefined && (window.$ = Zepto) + +;(function($){ + var _zid = 1, undefined, + slice = Array.prototype.slice, + isFunction = $.isFunction, + isString = function(obj){ return typeof obj == 'string' }, + handlers = {}, + specialEvents={}, + focusinSupported = 'onfocusin' in window, + focus = { focus: 'focusin', blur: 'focusout' }, + hover = { mouseenter: 'mouseenter', mouseleave: 'mouseleave' } + + specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' + + function zid(element) { + return element._zid || (element._zid = _zid++) + } + function findHandlers(element, event, fn, selector) { + event = parse(event) + if (event.ns) var matcher = matcherFor(event.ns) + return (handlers[zid(element)] || []).filter(function(handler) { + return handler + && (!event.e || handler.e == event.e) + && (!event.ns || matcher.test(handler.ns)) + && (!fn || zid(handler.fn) === zid(fn)) + && (!selector || handler.sel == selector) + }) + } + function parse(event) { + var parts = ('' + event).split('.') + return {e: parts[0], ns: parts.slice(1).sort().join(' ')} + } + function matcherFor(ns) { + return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') + } + + function eventCapture(handler, captureSetting) { + return handler.del && + (!focusinSupported && (handler.e in focus)) || + !!captureSetting + } + + function realEvent(type) { + return hover[type] || (focusinSupported && focus[type]) || type + } + + function add(element, events, fn, data, selector, delegator, capture){ + var id = zid(element), set = (handlers[id] || (handlers[id] = [])) + events.split(/\s/).forEach(function(event){ + if (event == 'ready') return $(document).ready(fn) + var handler = parse(event) + handler.fn = fn + handler.sel = selector + // emulate mouseenter, mouseleave + if (handler.e in hover) fn = function(e){ + var related = e.relatedTarget + if (!related || (related !== this && !$.contains(this, related))) + return handler.fn.apply(this, arguments) + } + handler.del = delegator + var callback = delegator || fn + handler.proxy = function(e){ + e = compatible(e) + if (e.isImmediatePropagationStopped()) return + e.data = data + var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)) + if (result === false) e.preventDefault(), e.stopPropagation() + return result + } + handler.i = set.length + set.push(handler) + if ('addEventListener' in element) + element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) + }) + } + function remove(element, events, fn, selector, capture){ + var id = zid(element) + ;(events || '').split(/\s/).forEach(function(event){ + findHandlers(element, event, fn, selector).forEach(function(handler){ + delete handlers[id][handler.i] + if ('removeEventListener' in element) + element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) + }) + }) + } + + $.event = { add: add, remove: remove } + + $.proxy = function(fn, context) { + var args = (2 in arguments) && slice.call(arguments, 2) + if (isFunction(fn)) { + var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) } + proxyFn._zid = zid(fn) + return proxyFn + } else if (isString(context)) { + if (args) { + args.unshift(fn[context], fn) + return $.proxy.apply(null, args) + } else { + return $.proxy(fn[context], fn) + } + } else { + throw new TypeError("expected function") + } + } + + $.fn.bind = function(event, data, callback){ + return this.on(event, data, callback) + } + $.fn.unbind = function(event, callback){ + return this.off(event, callback) + } + $.fn.one = function(event, selector, data, callback){ + return this.on(event, selector, data, callback, 1) + } + + var returnTrue = function(){return true}, + returnFalse = function(){return false}, + ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/, + eventMethods = { + preventDefault: 'isDefaultPrevented', + stopImmediatePropagation: 'isImmediatePropagationStopped', + stopPropagation: 'isPropagationStopped' + } + + function compatible(event, source) { + if (source || !event.isDefaultPrevented) { + source || (source = event) + + $.each(eventMethods, function(name, predicate) { + var sourceMethod = source[name] + event[name] = function(){ + this[predicate] = returnTrue + return sourceMethod && sourceMethod.apply(source, arguments) + } + event[predicate] = returnFalse + }) + + event.timeStamp || (event.timeStamp = Date.now()) + + if (source.defaultPrevented !== undefined ? source.defaultPrevented : + 'returnValue' in source ? source.returnValue === false : + source.getPreventDefault && source.getPreventDefault()) + event.isDefaultPrevented = returnTrue + } + return event + } + + function createProxy(event) { + var key, proxy = { originalEvent: event } + for (key in event) + if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] + + return compatible(proxy, event) + } + + $.fn.delegate = function(selector, event, callback){ + return this.on(event, selector, callback) + } + $.fn.undelegate = function(selector, event, callback){ + return this.off(event, selector, callback) + } + + $.fn.live = function(event, callback){ + $(document.body).delegate(this.selector, event, callback) + return this + } + $.fn.die = function(event, callback){ + $(document.body).undelegate(this.selector, event, callback) + return this + } + + $.fn.on = function(event, selector, data, callback, one){ + var autoRemove, delegator, $this = this + if (event && !isString(event)) { + $.each(event, function(type, fn){ + $this.on(type, selector, data, fn, one) + }) + return $this + } + + if (!isString(selector) && !isFunction(callback) && callback !== false) + callback = data, data = selector, selector = undefined + if (callback === undefined || data === false) + callback = data, data = undefined + + if (callback === false) callback = returnFalse + + return $this.each(function(_, element){ + if (one) autoRemove = function(e){ + remove(element, e.type, callback) + return callback.apply(this, arguments) + } + + if (selector) delegator = function(e){ + var evt, match = $(e.target).closest(selector, element).get(0) + if (match && match !== element) { + evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) + return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) + } + } + + add(element, event, callback, data, selector, delegator || autoRemove) + }) + } + $.fn.off = function(event, selector, callback){ + var $this = this + if (event && !isString(event)) { + $.each(event, function(type, fn){ + $this.off(type, selector, fn) + }) + return $this + } + + if (!isString(selector) && !isFunction(callback) && callback !== false) + callback = selector, selector = undefined + + if (callback === false) callback = returnFalse + + return $this.each(function(){ + remove(this, event, callback, selector) + }) + } + + $.fn.trigger = function(event, args){ + event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) + event._args = args + return this.each(function(){ + // handle focus(), blur() by calling them directly + if (event.type in focus && typeof this[event.type] == "function") this[event.type]() + // items in the collection might not be DOM elements + else if ('dispatchEvent' in this) this.dispatchEvent(event) + else $(this).triggerHandler(event, args) + }) + } + + // triggers event handlers on current element just as if an event occurred, + // doesn't trigger an actual event, doesn't bubble + $.fn.triggerHandler = function(event, args){ + var e, result + this.each(function(i, element){ + e = createProxy(isString(event) ? $.Event(event) : event) + e._args = args + e.target = element + $.each(findHandlers(element, event.type || event), function(i, handler){ + result = handler.proxy(e) + if (e.isImmediatePropagationStopped()) return false + }) + }) + return result + } + + // shortcut methods for `.bind(event, fn)` for each event type + ;('focusin focusout focus blur load resize scroll unload click dblclick '+ + 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ + 'change select keydown keypress keyup error').split(' ').forEach(function(event) { + $.fn[event] = function(callback) { + return (0 in arguments) ? + this.bind(event, callback) : + this.trigger(event) + } + }) + + $.Event = function(type, props) { + if (!isString(type)) props = type, type = props.type + var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true + if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) + event.initEvent(type, bubbles, true) + return compatible(event) + } + +})(Zepto) + +;(function($){ + var jsonpID = +new Date(), + document = window.document, + key, + name, + rscript = /)<[^<]*)*<\/script>/gi, + scriptTypeRE = /^(?:text|application)\/javascript/i, + xmlTypeRE = /^(?:text|application)\/xml/i, + jsonType = 'application/json', + htmlType = 'text/html', + blankRE = /^\s*$/, + originAnchor = document.createElement('a') + + originAnchor.href = window.location.href + + // trigger a custom event and return false if it was cancelled + function triggerAndReturn(context, eventName, data) { + var event = $.Event(eventName) + $(context).trigger(event, data) + return !event.isDefaultPrevented() + } + + // trigger an Ajax "global" event + function triggerGlobal(settings, context, eventName, data) { + if (settings.global) return triggerAndReturn(context || document, eventName, data) + } + + // Number of active Ajax requests + $.active = 0 + + function ajaxStart(settings) { + if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart') + } + function ajaxStop(settings) { + if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop') + } + + // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable + function ajaxBeforeSend(xhr, settings) { + var context = settings.context + if (settings.beforeSend.call(context, xhr, settings) === false || + triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false) + return false + + triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]) + } + function ajaxSuccess(data, xhr, settings, deferred) { + var context = settings.context, status = 'success' + settings.success.call(context, data, status, xhr) + if (deferred) deferred.resolveWith(context, [data, status, xhr]) + triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]) + ajaxComplete(status, xhr, settings) + } + // type: "timeout", "error", "abort", "parsererror" + function ajaxError(error, type, xhr, settings, deferred) { + var context = settings.context + settings.error.call(context, xhr, type, error) + if (deferred) deferred.rejectWith(context, [xhr, type, error]) + triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type]) + ajaxComplete(type, xhr, settings) + } + // status: "success", "notmodified", "error", "timeout", "abort", "parsererror" + function ajaxComplete(status, xhr, settings) { + var context = settings.context + settings.complete.call(context, xhr, status) + triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]) + ajaxStop(settings) + } + + function ajaxDataFilter(data, type, settings) { + if (settings.dataFilter == empty) return data + var context = settings.context + return settings.dataFilter.call(context, data, type) + } + + // Empty function, used as default callback + function empty() {} + + $.ajaxJSONP = function(options, deferred){ + if (!('type' in options)) return $.ajax(options) + + var _callbackName = options.jsonpCallback, + callbackName = ($.isFunction(_callbackName) ? + _callbackName() : _callbackName) || ('Zepto' + (jsonpID++)), + script = document.createElement('script'), + originalCallback = window[callbackName], + responseData, + abort = function(errorType) { + $(script).triggerHandler('error', errorType || 'abort') + }, + xhr = { abort: abort }, abortTimeout + + if (deferred) deferred.promise(xhr) + + $(script).on('load error', function(e, errorType){ + clearTimeout(abortTimeout) + $(script).off().remove() + + if (e.type == 'error' || !responseData) { + ajaxError(null, errorType || 'error', xhr, options, deferred) + } else { + ajaxSuccess(responseData[0], xhr, options, deferred) + } + + window[callbackName] = originalCallback + if (responseData && $.isFunction(originalCallback)) + originalCallback(responseData[0]) + + originalCallback = responseData = undefined + }) + + if (ajaxBeforeSend(xhr, options) === false) { + abort('abort') + return xhr + } + + window[callbackName] = function(){ + responseData = arguments + } + + script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName) + document.head.appendChild(script) + + if (options.timeout > 0) abortTimeout = setTimeout(function(){ + abort('timeout') + }, options.timeout) + + return xhr + } + + $.ajaxSettings = { + // Default type of request + type: 'GET', + // Callback that is executed before request + beforeSend: empty, + // Callback that is executed if the request succeeds + success: empty, + // Callback that is executed the the server drops error + error: empty, + // Callback that is executed on request complete (both: error and success) + complete: empty, + // The context for the callbacks + context: null, + // Whether to trigger "global" Ajax events + global: true, + // Transport + xhr: function () { + return new window.XMLHttpRequest() + }, + // MIME types mapping + // IIS returns Javascript as "application/x-javascript" + accepts: { + script: 'text/javascript, application/javascript, application/x-javascript', + json: jsonType, + xml: 'application/xml, text/xml', + html: htmlType, + text: 'text/plain' + }, + // Whether the request is to another domain + crossDomain: false, + // Default timeout + timeout: 0, + // Whether data should be serialized to string + processData: true, + // Whether the browser should be allowed to cache GET responses + cache: true, + //Used to handle the raw response data of XMLHttpRequest. + //This is a pre-filtering function to sanitize the response. + //The sanitized response should be returned + dataFilter: empty + } + + function mimeToDataType(mime) { + if (mime) mime = mime.split(';', 2)[0] + return mime && ( mime == htmlType ? 'html' : + mime == jsonType ? 'json' : + scriptTypeRE.test(mime) ? 'script' : + xmlTypeRE.test(mime) && 'xml' ) || 'text' + } + + function appendQuery(url, query) { + if (query == '') return url + return (url + '&' + query).replace(/[&?]{1,2}/, '?') + } + + // serialize payload and append it to the URL for GET requests + function serializeData(options) { + if (options.processData && options.data && $.type(options.data) != "string") + options.data = $.param(options.data, options.traditional) + if (options.data && (!options.type || options.type.toUpperCase() == 'GET' || 'jsonp' == options.dataType)) + options.url = appendQuery(options.url, options.data), options.data = undefined + } + + $.ajax = function(options){ + var settings = $.extend({}, options || {}), + deferred = $.Deferred && $.Deferred(), + urlAnchor, hashIndex + for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key] + + ajaxStart(settings) + + if (!settings.crossDomain) { + urlAnchor = document.createElement('a') + urlAnchor.href = settings.url + // cleans up URL for .href (IE only), see https://github.com/madrobby/zepto/pull/1049 + urlAnchor.href = urlAnchor.href + settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host) + } + + if (!settings.url) settings.url = window.location.toString() + if ((hashIndex = settings.url.indexOf('#')) > -1) settings.url = settings.url.slice(0, hashIndex) + serializeData(settings) + + var dataType = settings.dataType, hasPlaceholder = /\?.+=\?/.test(settings.url) + if (hasPlaceholder) dataType = 'jsonp' + + if (settings.cache === false || ( + (!options || options.cache !== true) && + ('script' == dataType || 'jsonp' == dataType) + )) + settings.url = appendQuery(settings.url, '_=' + Date.now()) + + if ('jsonp' == dataType) { + if (!hasPlaceholder) + settings.url = appendQuery(settings.url, + settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?') + return $.ajaxJSONP(settings, deferred) + } + + var mime = settings.accepts[dataType], + headers = { }, + setHeader = function(name, value) { headers[name.toLowerCase()] = [name, value] }, + protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol, + xhr = settings.xhr(), + nativeSetHeader = xhr.setRequestHeader, + abortTimeout + + if (deferred) deferred.promise(xhr) + + if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest') + setHeader('Accept', mime || '*/*') + if (mime = settings.mimeType || mime) { + if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0] + xhr.overrideMimeType && xhr.overrideMimeType(mime) + } + if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET')) + setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded') + + if (settings.headers) for (name in settings.headers) setHeader(name, settings.headers[name]) + xhr.setRequestHeader = setHeader + + xhr.onreadystatechange = function(){ + if (xhr.readyState == 4) { + xhr.onreadystatechange = empty + clearTimeout(abortTimeout) + var result, error = false + if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) { + dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type')) + + if (xhr.responseType == 'arraybuffer' || xhr.responseType == 'blob') + result = xhr.response + else { + result = xhr.responseText + + try { + // http://perfectionkills.com/global-eval-what-are-the-options/ + // sanitize response accordingly if data filter callback provided + result = ajaxDataFilter(result, dataType, settings) + if (dataType == 'script') (1,eval)(result) + else if (dataType == 'xml') result = xhr.responseXML + else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result) + } catch (e) { error = e } + + if (error) return ajaxError(error, 'parsererror', xhr, settings, deferred) + } + + ajaxSuccess(result, xhr, settings, deferred) + } else { + ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred) + } + } + } + + if (ajaxBeforeSend(xhr, settings) === false) { + xhr.abort() + ajaxError(null, 'abort', xhr, settings, deferred) + return xhr + } + + var async = 'async' in settings ? settings.async : true + xhr.open(settings.type, settings.url, async, settings.username, settings.password) + + if (settings.xhrFields) for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name] + + for (name in headers) nativeSetHeader.apply(xhr, headers[name]) + + if (settings.timeout > 0) abortTimeout = setTimeout(function(){ + xhr.onreadystatechange = empty + xhr.abort() + ajaxError(null, 'timeout', xhr, settings, deferred) + }, settings.timeout) + + // avoid sending empty string (#319) + xhr.send(settings.data ? settings.data : null) + return xhr + } + + // handle optional data/success arguments + function parseArguments(url, data, success, dataType) { + if ($.isFunction(data)) dataType = success, success = data, data = undefined + if (!$.isFunction(success)) dataType = success, success = undefined + return { + url: url + , data: data + , success: success + , dataType: dataType + } + } + + $.get = function(/* url, data, success, dataType */){ + return $.ajax(parseArguments.apply(null, arguments)) + } + + $.post = function(/* url, data, success, dataType */){ + var options = parseArguments.apply(null, arguments) + options.type = 'POST' + return $.ajax(options) + } + + $.getJSON = function(/* url, data, success */){ + var options = parseArguments.apply(null, arguments) + options.dataType = 'json' + return $.ajax(options) + } + + $.fn.load = function(url, data, success){ + if (!this.length) return this + var self = this, parts = url.split(/\s/), selector, + options = parseArguments(url, data, success), + callback = options.success + if (parts.length > 1) options.url = parts[0], selector = parts[1] + options.success = function(response){ + self.html(selector ? + $('
').html(response.replace(rscript, "")).find(selector) + : response) + callback && callback.apply(self, arguments) + } + $.ajax(options) + return this + } + + var escape = encodeURIComponent + + function serialize(params, obj, traditional, scope){ + var type, array = $.isArray(obj), hash = $.isPlainObject(obj) + $.each(obj, function(key, value) { + type = $.type(value) + if (scope) key = traditional ? scope : + scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']' + // handle data in serializeArray() format + if (!scope && array) params.add(value.name, value.value) + // recurse into nested objects + else if (type == "array" || (!traditional && type == "object")) + serialize(params, value, traditional, key) + else params.add(key, value) + }) + } + + $.param = function(obj, traditional){ + var params = [] + params.add = function(key, value) { + if ($.isFunction(value)) value = value() + if (value == null) value = "" + this.push(escape(key) + '=' + escape(value)) + } + serialize(params, obj, traditional) + return params.join('&').replace(/%20/g, '+') + } +})(Zepto) + +;(function($){ + $.fn.serializeArray = function() { + var name, type, result = [], + add = function(value) { + if (value.forEach) return value.forEach(add) + result.push({ name: name, value: value }) + } + if (this[0]) $.each(this[0].elements, function(_, field){ + type = field.type, name = field.name + if (name && field.nodeName.toLowerCase() != 'fieldset' && + !field.disabled && type != 'submit' && type != 'reset' && type != 'button' && type != 'file' && + ((type != 'radio' && type != 'checkbox') || field.checked)) + add($(field).val()) + }) + return result + } + + $.fn.serialize = function(){ + var result = [] + this.serializeArray().forEach(function(elm){ + result.push(encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value)) + }) + return result.join('&') + } + + $.fn.submit = function(callback) { + if (0 in arguments) this.bind('submit', callback) + else if (this.length) { + var event = $.Event('submit') + this.eq(0).trigger(event) + if (!event.isDefaultPrevented()) this.get(0).submit() + } + return this + } + +})(Zepto) + +;(function(){ + // getComputedStyle shouldn't freak out when called + // without a valid element as argument + try { + getComputedStyle(undefined) + } catch(e) { + var nativeGetComputedStyle = getComputedStyle + window.getComputedStyle = function(element, pseudoElement){ + try { + return nativeGetComputedStyle(element, pseudoElement) + } catch(e) { + return null + } + } + } +})() + return Zepto +})) diff --git a/examples/resx/resx.go b/examples/resx/resx.go new file mode 100644 index 0000000..b290760 --- /dev/null +++ b/examples/resx/resx.go @@ -0,0 +1,31 @@ +package resx + +import ( + "embed" + "github.com/sciter-sdk/go-sciter" + "github.com/sciter-sdk/go-sciter/resx" + "github.com/sciter-sdk/go-sciter/window" + "log" +) + + +// embed resource directory + +//go:embed html/* +var f embed.FS + +func main() { + win, err := window.New(sciter.DefaultWindowCreateFlag, nil) + if err != nil { + log.Fatal(err) + } + sciter.SetOption(sciter.SCITER_SET_DEBUG_MODE, 1) + //use embed resource + resx.HandleDataLoad(win.Sciter, f) + //load embed file + win.LoadFile("resx://html/index.html") + win.SetTitle("title") + win.Show() + win.Run() + log.Println(".....exit.....") +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1df194a --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/sciter-sdk/go-sciter + +go 1.16 + +require ( + github.com/GeertJohan/go.rice v1.0.2 + github.com/lxn/win v0.0.0-20210218163916-a377121e959e +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4aca5f7 --- /dev/null +++ b/go.sum @@ -0,0 +1,15 @@ +github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= +github.com/GeertJohan/go.rice v1.0.2 h1:PtRw+Tg3oa3HYwiDBZyvOJ8LdIyf6lAovJJtr7YOAYk= +github.com/GeertJohan/go.rice v1.0.2/go.mod h1:af5vUNlDNkCjOZeSGFgIJxDje9qdjsO6hshx0gTmZt4= +github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= +github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= +github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc= +github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= +github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13 h1:5jaG59Zhd+8ZXe8C+lgiAGqkOaZBruqrWclLkgAww34= +golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/include/aux-asset.h b/include/aux-asset.h new file mode 100644 index 0000000..882c214 --- /dev/null +++ b/include/aux-asset.h @@ -0,0 +1,101 @@ +#pragma once + +#if defined(_WIN32) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #define _WINSOCKAPI_ + #include +#endif + +#include +#include "sciter-om.h" + +namespace aux { + + using namespace sciter::om; + +} + +#if defined(WINDOWS) + +namespace com { + + //asset - yet another smart pointer + template + class ptr + { + protected: + T* p; + + public: + typedef T asset_t; + + ptr():p(0) {} + ptr(T* lp):p(0) { if (lp) (p = lp)->AddRef(); } + ptr(const ptr& cp):p(0) { if (cp.p) (p = cp.p)->AddRef(); } + + ~ptr() + { + ULONG c = 0; + if (p) + c = p->Release(); + } + operator T*() const { return p; } + T* operator->() const { assert(p != 0); return p; } + + // used as target T** pointer to pointer - in places receiving newly created objects (initially add-refed) + T** target() { release(); return &p; } + + bool operator!() const { return p == 0; } + operator bool() const { return p != 0; } + bool operator!=(T* pT) const { return p != pT; } + bool operator==(T* pT) const { return p == pT; } + + // release the interface and set it to NULL + void release() { if (p) { T* pt = p; p = 0; pt->Release(); }} + + // attach to an existing interface (does not AddRef) + void attach(T* p2) { release(); p = p2; } + // detach the interface (does not Release) + T* detach() { T* pt = p; p = 0; return pt; } + + static T* assign(T* &pp, T* lp) + { + if (lp != 0) lp->AddRef(); + if (pp) pp->Release(); + pp = lp; + return lp; + } + + T* operator=(T* lp) { if(p != lp) return assign(p, lp); return p; } + T* operator=(const ptr& lp) { if(p != lp) return assign(p, lp.p); return p; } + + T* acquire() { if( p ) p->AddRef(); return p; } + + HRESULT CoCreateInstance (REFCLSID classUUID, DWORD dwClsContext = CLSCTX_INPROC_SERVER) + { + HRESULT hr = ::CoCreateInstance (classUUID, 0, dwClsContext, __uuidof (T), (void**) target()); + assert (hr != CO_E_NOTINITIALIZED); // You haven't called CoInitialize for the current thread! + return hr; + } + + template + HRESULT QueryInterface (REFCLSID classUUID, ptr& dest_object) const + { + if (p == 0) + return E_POINTER; + return p->QueryInterface (classUUID, (void**) dest_object.target()); + } + + template + HRESULT QueryInterface (ptr& dest_object) const + { + return this->QueryInterface (__uuidof (OTHER_COM_CLASS), dest_object); + } + + }; + +} + +#endif diff --git a/include/aux-cvt.h b/include/aux-cvt.h new file mode 100644 index 0000000..5abe499 --- /dev/null +++ b/include/aux-cvt.h @@ -0,0 +1,835 @@ +#ifndef __sciter_aux_h__ +#define __sciter_aux_h__ + +#if defined(__cplusplus) && !defined( PLAIN_API_ONLY ) + +/* + * Terra Informatica Sciter and HTMLayout Engines + * http://terrainformatica.com/sciter, http://terrainformatica.com/htmlayout + * + * basic primitives. + * + * The code and information provided "as-is" without + * warranty of any kind, either expressed or implied. + * + * (C) 2003-2015, Andrew Fedoniouk (andrew@terrainformatica.com) + */ + +/**\file + * \brief primitives + **/ + +/* + + pod::copy - memcpy wrapper + pod::move - memmove wrapper + pod::buffer - dynamic buffer, string builder, etc. + + utf8::towcs() - utf8 to WCHAR* converter + utf8::fromwcs() - WCHAR* to utf8 converter + utf8::ostream - raw ASCII/UNICODE -> UTF8 converter + utf8::oxstream - ASCII/UNICODE -> UTF8 converter with XML support + + inline bool streq(const char* s, const char* s1) - NULL safe string comparison function + inline bool wcseq(const WCHAR* s, const WCHAR* s1) - NULL safe wide string comparison function + inline bool streqi(const char* s, const char* s1) - the same, but case independent + inline bool wcseqi(const WCHAR* s, const WCHAR* s1) - the same, but case independent + + w2a - helper object for const WCHAR* to const char* conversion + a2w - helper object for const char* to const WCHAR* conversion + w2utf - helper object for const WCHAR* to utf8 conversion + utf2w - helper object for utf8 to const WCHAR* conversion + + t2w - const TCHAR* to const WCHAR* conversion, #definition + w2t - const WCHAR* to const TCHAR* conversion, #definition + + itoa, itow - int to const char* converter + atoi, wtoi - const char* to int converter (parser) + ftoa, ftow - double to const char* converter + + + */ + +#pragma once + +#include +#include +#include +#include +#include +#include // std::reverse +#include "aux-platform.h" +#include "aux-slice.h" + +#include +#include + +// disable that warnings in VC 2005 +#pragma warning(disable:4786) //identifier was truncated... +#pragma warning(disable:4100) //unreferenced formal parameter + +#ifndef BYTE + typedef unsigned char BYTE; +#endif + +//#include "aux-slice.h" + +// WARNING: macros below must be used only for passing parameters to functions! + +/* +#if !defined(W2A) // WCHAR to multi-BYTE string converter (current locale) +#define W2A aux::w2a +#endif + +#if !defined(A2W) // multi-BYTE to WCHAR string converter (current locale) +#define A2W aux::a2w +#endif + +#if !defined(UTF2W) // utf-8 to WCHAR string converter +#define UTF2W aux::utf2w +#endif + +#if !defined(W2UTF) // WCHAR to utf-8 string converter +#define W2UTF aux::w2utf +#endif + +#if !defined(W2T) +#if !defined(UNICODE) +#define W2T(S) aux::w2a(S) +#else +#define W2T(S) (S) +#endif +#endif + +#if !defined(T2W) +#if !defined(UNICODE) +#define T2W(S) aux::a2w(S) +#else +#define T2W(S) (S) +#endif +#endif + +#if !defined(A2T) +#if !defined(UNICODE) +#define A2T(S) (S) +#else +#define A2T(S) aux::a2w(S) +#endif +#endif + +#if !defined(T2A) +#if !defined(UNICODE) +#define T2A(S) (S) +#else +#define T2A(S) aux::w2a(S) +#endif +#endif + +*/ + +#ifdef UNICODE +#define a2t( S ) aux::a2w(S) +#define t2a( S ) aux::w2a(S) +#define w2t( S ) (S) +#define t2w( S ) (S) +#define t2i( S ) aux::wtoi(S,0) +#define i2t( S ) aux::itow(S) +#else +#define a2t( S ) (S) +#define t2a( S ) (S) +#define w2t( S ) aux::w2a(S) +#define t2w( S ) aux::a2w(S) +#define t2i( S ) aux::atoi(S,0) +#define i2t( S ) aux::itoa(S) +#endif + +#define w2u( S ) aux::w2utf(S) +#define u2w( S ) aux::utf2w(S) + +#define i2a( I ) aux::itoa(I) +#define i2w( I ) aux::itow(I) + +inline void* zalloc ( size_t sz) +{ + void* p = malloc(sz); + if(p) memset(p,0,sz); + return p; +} + +//elements in array literal +#define ITEMS_IN(a) (sizeof(a)/sizeof(a[0])) +//chars in sting literal +#define CHARS_IN(s) (sizeof(s) / sizeof(s[0]) - 1) + +/**pod namespace - POD primitives. **/ +namespace pod +{ + template void copy ( T* dst, const T* src, size_t nelements) + { + memcpy(dst,src,nelements*sizeof(T)); + } + template void move ( T* dst, const T* src, size_t nelements) + { + memmove(dst,src,nelements*sizeof(T)); + } + + /** buffer - in-memory dynamic buffer implementation. **/ + template + class buffer + { + T* _body; + size_t _allocated; + size_t _size; + + T* reserve(size_t size) + { + size_t newsize = _size + size; + if( newsize > _allocated ) + { + _allocated = (_allocated * 3) / 2; + if(_allocated < newsize) _allocated = newsize; + T *newbody = new T[_allocated]; + copy(newbody,_body,_size); + delete[] _body; + _body = newbody; + } + return _body + _size; + } + + public: + + buffer():_size(0) { _body = new T[_allocated = 256]; } + ~buffer() { delete[] _body; } + + const T * data() const + { + buffer* self = const_cast(this); + if(_size == _allocated) self->reserve(1); + self->_body[_size] = 0; return _body; + } + + size_t length() const { return _size; } + + void push(T c) { *reserve(1) = c; ++_size; } + void push(const T *pc, size_t sz) { copy(reserve(sz),pc,sz); _size += sz; } + + void clear() { _size = 0; } + + }; + + typedef buffer byte_buffer; + typedef buffer wchar_buffer; + typedef buffer char_buffer; +} + +namespace utf8 +{ + // convert utf8 code unit sequence to WCHAR sequence + + inline bool towcs(const BYTE *utf8, size_t length, pod::wchar_buffer& outbuf) + { + if(!utf8 || length == 0) return true; + const BYTE* pc = (const BYTE*)utf8; + const BYTE* last = pc + length; + unsigned int b; + unsigned int num_errors = 0; + while (pc < last) + { + b = *pc++; + + if( !b ) break; // 0 - is eos in all utf encodings + + if ((b & 0x80) == 0) + { + // 1-BYTE sequence: 000000000xxxxxxx = 0xxxxxxx + ; + } + else if ((b & 0xe0) == 0xc0) + { + // 2-BYTE sequence: 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx + if(pc == last) { outbuf.push('?'); ++num_errors; break; } + b = (b & 0x1f) << 6; + b |= (*pc++ & 0x3f); + } + else if ((b & 0xf0) == 0xe0) + { + // 3-BYTE sequence: zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx + if(pc >= last - 1) { outbuf.push('?'); ++num_errors; break; } + + b = (b & 0x0f) << 12; + b |= (*pc++ & 0x3f) << 6; + b |= (*pc++ & 0x3f); + if(b == 0xFEFF && + outbuf.length() == 0) // bom at start + continue; // skip it + } + else if ((b & 0xf8) == 0xf0) + { + // 4-BYTE sequence: 11101110wwwwzzzzyy + 110111yyyyxxxxxx = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx + if(pc >= last - 2) { outbuf.push('?'); break; } + + b = (b & 0x07) << 18; + b |= (*pc++ & 0x3f) << 12; + b |= (*pc++ & 0x3f) << 6; + b |= (*pc++ & 0x3f); + // b shall contain now full 21-bit unicode code point. + assert((b & 0x1fffff) == b); + if((b & 0x1fffff) != b) + { + outbuf.push('?'); + ++num_errors; + continue; + } + outbuf.push( WCHAR(0xd7c0 + (b >> 10)) ); + outbuf.push( WCHAR(0xdc00 | (b & 0x3ff)) ); + continue; + } + else + { + assert(0); //bad start of UTF-8 multi-BYTE sequence" + ++num_errors; + b = '?'; + } + outbuf.push( WCHAR(b) ); + } + return num_errors == 0; + } + + // gets full unicode code point from UTF16 sequence + inline bool get_ucp(aux::wchars &buf, unsigned int& ucp) + { + if (buf.length == 0) + return false; + WCHAR c = *buf.start; + buf.prune(1); + if (c < 0xD800 || c > 0xDBFF) { + ucp = c; + return true; // not a surrogate pair + } + if (buf.length == 0) { + assert(false); // surrogate pair is not complete + ucp = c; + return false; + } + WCHAR nc = *buf.start; + buf.prune(1); + ucp = (c - 0xD800) * 0x400 + (nc - 0xDC00) + 0x10000; + return true; + } + + // gets full unicode code point from UTF16 sequence + inline bool get_ucp(const WCHAR* &pc, unsigned int& ucp) + { + if (*pc == 0) + return false; + WCHAR c = *pc++; + if (c < 0xD800 || c > 0xDBFF) { + ucp = c; + return true; // not a surrogate pair + } + if (*pc == 0) { + assert(false); // surrogate pair is not complete + ucp = 0; + return false; + } + WCHAR nc = *pc++; + ucp = (c - 0xD800) * 0x400 + (nc - 0xDC00) + 0x10000; + return true; + } + + inline bool fromwcs(aux::wchars buf, pod::byte_buffer& outbuf) + { + unsigned int num_errors = 0; + unsigned int c; // unicode code point + + while(get_ucp(buf,c)) + { + if (c < (1 << 7)) + { + outbuf.push(BYTE(c)); + } + else if (c < (1 << 11)) + { + outbuf.push(BYTE((c >> 6) | 0xc0)); + outbuf.push(BYTE((c & 0x3f) | 0x80)); + } + else if (c < (1 << 16)) + { + outbuf.push(BYTE((c >> 12) | 0xe0)); + outbuf.push(BYTE(((c >> 6) & 0x3f) | 0x80)); + outbuf.push(BYTE((c & 0x3f) | 0x80)); + } + else if (c < (1 << 21)) + { + outbuf.push(BYTE((c >> 18) | 0xf0)); + outbuf.push(BYTE(((c >> 12) & 0x3f) | 0x80)); + outbuf.push(BYTE(((c >> 6) & 0x3f) | 0x80)); + outbuf.push(BYTE((c & 0x3f) | 0x80)); + } + else + ++num_errors; + } + return num_errors == 0; + } + + // UTF8 stream + + // class T must have two methods: + // void push(unsigned char c) + // void push(const unsigned char *pc, size_t sz) + + // bool X - true - XML markup character conversion (characters '<','>',etc). + // false - no conversion at all. + + template + class ostream_t : public T + { + public: + ostream_t() + { + // utf8 BYTE order mark + static unsigned char BOM[] = { 0xEF, 0xBB, 0xBF }; + T::push(BOM, sizeof(BOM)); + } + + // intended to handle only ascii-7 strings + // use this for markup output + ostream_t& operator << (const char* str) + { + T::push((const unsigned char*)str,strlen(str)); return *this; + } + + ostream_t& operator << (char c) + { + T::push((unsigned char)c); return *this; + } + + // use UNICODE chars for value output + ostream_t& operator << (const WCHAR* wstr) + { + const WCHAR* p = wstr; + unsigned int c; + while(get_ucp(p,c)) + { + if(X) + switch(c) + { + case '<': *this << "<"; continue; + case '>': *this << ">"; continue; + case '&': *this << "&"; continue; + case '"': *this << """; continue; + case '\'': *this << "'"; continue; + } + if (c < (1 << 7)) + { + T::push (BYTE(c)); + } + else if (c < (1 << 11)) { + T::push (BYTE((c >> 6) | 0xc0)); + T::push (BYTE((c & 0x3f) | 0x80)); + } + else if (c < (1 << 16)) { + T::push (BYTE((c >> 12) | 0xe0)); + T::push (BYTE(((c >> 6) & 0x3f) | 0x80)); + T::push (BYTE((c & 0x3f) | 0x80)); + } + else if (c < (1 << 21)) + { + T::push (BYTE((c >> 18) | 0xf0)); + T::push (BYTE(((c >> 12) & 0x3f) | 0x80)); + T::push (BYTE(((c >> 6) & 0x3f) | 0x80)); + T::push (BYTE((c & 0x3f) | 0x80)); + } + } + return *this; + } + ostream_t& operator << (const std::wstring& str) + { + return *this << (str.c_str()); + } + + }; + + // raw ASCII/UNICODE -> UTF8 converter + typedef ostream_t ostream; + // ASCII/UNICODE -> UTF8 converter with XML support + typedef ostream_t oxstream; + + +} // namespace utf8 + +namespace aux +{ + template struct slice; + + template + inline T + limit ( T v, T minv, T maxv ) + { + assert(minv < maxv); + if (minv >= maxv) + return minv; + if (v > maxv) return maxv; + if (v < minv) return minv; + return v; + } + + // safe string comparison + inline bool streq(const char* s, const char* s1) + { + if( s && s1 ) + return strcmp(s,s1) == 0; + return false; + } + + // safe wide string comparison +/* inline bool wcseq(const WCHAR* s, const WCHAR* s1) + { + if( s && s1 ) + return std::wcscmp(s,s1) == 0; + return false; + } */ + + // safe case independent string comparison + inline bool streqi(const char* s, const char* s1) + { + if( s && s1 ) + + return stricmp(s,s1) == 0; + return false; + } + + // safe case independent wide string comparison +/* inline bool wcseqi(const WCHAR* s, const WCHAR* s1) + { + if( s && s1 ) + return wcsicmp(s,s1) == 0; + return false; + } */ + + // helper convertor objects WCHAR to utf8 and vice versa + class utf2w + { + pod::wchar_buffer buffer; + public: + explicit utf2w(const BYTE* utf8, size_t length = 0) + { + if(utf8) + { + if( length == 0) length = strlen((const char*)utf8); + utf8::towcs(utf8, length ,buffer); + } + } + explicit utf2w(const char* utf8, size_t length = 0) + { + if(utf8) + { + if( length == 0) length = strlen(utf8); + utf8::towcs((const BYTE*)utf8, length ,buffer); + } + } + explicit utf2w(const std::string& utf8) + { + utf8::towcs((const BYTE*)utf8.c_str(), utf8.length() ,buffer); + } + + ~utf2w() {} + + operator const WCHAR*() const { return buffer.data(); } + const WCHAR* c_str() const { return buffer.data(); } + size_t length() const { return buffer.length(); } + + pod::wchar_buffer& get_buffer() { return buffer; } + + operator aux::wchars() const { return aux::wchars(buffer.data(),buffer.length()); } + aux::wchars chars() const { return aux::wchars(buffer.data(),buffer.length()); } + //operator std::basic_string() const { return std::basic_string(buffer.data(),buffer.length()); } + + }; + + class w2utf + { + pod::byte_buffer buffer; + public: + explicit w2utf(const WCHAR* wstr) + { + if(wstr) + { + utf8::fromwcs(chars_of(wstr),buffer); + } + } + explicit w2utf(const std::basic_string& str) + { + utf8::fromwcs(chars_of(str),buffer); + } + explicit w2utf(wchars str) + { + utf8::fromwcs(str, buffer); + } + ~w2utf() {} + operator const BYTE*() const { return buffer.data(); } + operator const char*() const { return (const char*)buffer.data(); } + //operator std::string() const { return std::string(c_str(),length()); } + + const char* c_str() const { return (const char*)buffer.data(); } + chars operator()() const { return chars(c_str(),length()); } + + size_t length() const { return buffer.length(); } + }; + +#ifdef WINDOWS + + // helper convertor objects WCHAR to ANSI string and vice versa + class w2a + { + char local[16]; + char* buffer; + size_t n; + + void init(const WCHAR* wstr, size_t nu) + { + n = WideCharToMultiByte(CP_ACP,0,wstr,int(nu),0,0,0,0); + buffer = (n < (16-1))? local:new char[n+1]; + WideCharToMultiByte(CP_ACP,0,wstr,int(nu),buffer,int(n),0,0); + buffer[n] = 0; + } + public: + explicit w2a(const WCHAR* wstr):buffer(0),n(0) + { + if(wstr) + init(wstr,wcslen(wstr)); + } + explicit w2a(const std::basic_string& wstr):buffer(0),n(0) + { + init(wstr.c_str(),wstr.length()); + } + explicit w2a(slice s):buffer(0), n(0) + { + init(s.start,s.length); + } + ~w2a() { if(buffer != local) delete[] buffer; } + size_t length() const { return n; } + operator const char*() { return buffer; } + const char* c_str() const { return buffer; } + }; + + class a2w + { + WCHAR local[16]; + WCHAR* buffer; + size_t nu; + void init(const char* str, size_t n) + { +#ifdef _WIN32_WCE + nu = MultiByteToWideChar(CP_ACP,0,str,n,0,0); +#else + nu = MultiByteToWideChar(CP_THREAD_ACP,0,str,int(n),0,0); +#endif + buffer = ( nu < (16-1) )? local: new WCHAR[nu+1]; +#ifdef _WIN32_WCE + MultiByteToWideChar(CP_ACP,0,str,n,buffer,nu); +#else + MultiByteToWideChar(CP_THREAD_ACP,0,str,int(n),buffer,int(nu)); +#endif + buffer[nu] = 0; + } + public: + explicit a2w(const char* str):buffer(0), nu(0) + { + if(str) + init(str, strlen(str)); + } + explicit a2w(slice s):buffer(0), nu(0) + { + init(s.start,s.length); + } + explicit a2w(const std::string& s):buffer(0), nu(0) + { + init(s.c_str(),s.length()); + } + + ~a2w() { if(buffer != local) delete[] buffer; } + size_t length() const { return nu; } + operator const WCHAR*() const { return buffer; } + const WCHAR* c_str() const { return buffer; } + operator aux::wchars() const { return aux::wchars(buffer,nu); } + aux::wchars chars() const { return aux::wchars(buffer,nu); } + //operator std::basic_string() const { return std::basic_string(buffer,nu); } + + }; + + class w2oem + { + char local[64]; + char* buffer; + size_t n; + + void init(const WCHAR* wstr, size_t nu) + { + n = WideCharToMultiByte(CP_OEMCP,0,wstr,int(nu),0,0,0,0); + buffer = (n < (64-1))? local : new char[n+1]; + WideCharToMultiByte(CP_OEMCP,0,wstr,int(nu),buffer,int(n),0,0); + buffer[n] = 0; + } + + w2oem(const w2oem &); + w2oem& operator=(const w2oem &); + + public: + explicit w2oem(const WCHAR* wstr):buffer(0),n(0) + { + local[0] = 0; + if(wstr) + init(wstr,wcslen(wstr)); + } + explicit w2oem(const WCHAR* wstr, size_t sz) :buffer(0), n(0) + { + local[0] = 0; + if (wstr) + init(wstr, sz); + } + explicit w2oem(const std::wstring& wstr):buffer(0),n(0) + { + local[0] = 0; + init(wstr.c_str(),wstr.length()); + } + explicit w2oem(slice s); + + ~w2oem() { if(buffer != local) delete[] buffer; } + size_t length() const { return n; } + operator const char*() { return buffer; } + const char* c_str() const { return buffer; } + }; + +#elif defined(UTF8_CHARS) + typedef utf2w a2w; + typedef w2utf w2a; + typedef utf2w oem2w; + typedef w2utf w2oem; +#else + #error "unknown char string representation" +#endif + + /** Integer to string converter. + Use it as wostream << itow(234) + **/ + +template + class itot + { + CT buffer[38]; + public: + itot(int n, int radix = 10) + { + static char num[] = "0123456789abcdefghijklmnopqrstuvwxyz"; + CT* wstr = buffer; + if (radix < 2 || radix > 35) { *wstr = 0; return; } // validate radix + int sign = n; if (sign < 0) n = -n; + do { *wstr++ = num[n % radix]; } while( n /= radix ); // conversion. number is reversed. + if(sign<0) *wstr++ ='-'; + *wstr = 0; + // reverse the string + std::reverse(buffer,wstr); + } + operator const CT*() { return buffer; } + }; + + typedef itot itoa; + typedef itot itow; + + + /** Float to string converter. + Use it as ostream << ftoa(234.1); or + Use it as ostream << ftoa(234.1,"pt"); or + **/ + class ftoa + { + char buffer[64]; + public: + ftoa(double d, const char* units = "", int fractional_digits = 1) + { + snprintf(buffer, sizeof(buffer), "%.*f%s", fractional_digits, d, units ); + buffer[63] = 0; + } + operator const char*() { return buffer; } + }; + + /** Float to wstring converter. + Use it as wostream << ftow(234.1); or + Use it as wostream << ftow(234.1,"pt"); or + **/ + /*class ftow + { + WCHAR buffer[64]; + public: + ftow(double d, const WCHAR* units = "", int fractional_digits = 1) + { +#ifdef WINDOWS + _snwprintf_s(buffer, 64, L"%.*f%s", fractional_digits, d, units ); +#else + swprintf(buffer, 64, L"%.*f%s", fractional_digits, d, units ); +#endif + buffer[63] = 0; + } + operator const WCHAR*() { return buffer; } + };*/ + + /** string to integer parser. **/ + inline int atoi(const char *s, int default_value = 0) + { + if( !s ) return default_value; + char *lastptr; + long i = strtol( s, &lastptr, 10 ); + return (lastptr != s)? (int)i : default_value; + } + + /** wstring to integer parser. **/ + inline int wtoi(const WCHAR *s, int default_value = 0) + { + w2a at(s); + return atoi(at.c_str(),default_value); + } + + // class T must have two methods: + // void push(WCHAR c) + // void push(const WCHAR *pc, size_t sz) + template + class ostream_t : public T + { + public: + ostream_t() {} + + // intended to handle only ascii-7 strings + // use this for markup output + ostream_t& operator << (const char* str) + { + if(!str) return *this; + while( *str ) T::push(*str++); + return *this; + } + + ostream_t& operator << (char c) + { + T::push(c); return *this; + } + + // intended to handle only ascii-7 strings + // use this for markup output + ostream_t& operator << (const WCHAR* str) + { + if(!str || !str[0]) return *this; + T::push(str,wcslen(str)); return *this; + } + + ostream_t& operator << (WCHAR c) + { + T::push(c); return *this; + } + }; + + // wostream - a.k.a. wstring builder - buffer for dynamic composition of WCHAR strings + typedef ostream_t wostream; +} + +#endif +#endif diff --git a/include/aux-platform.h b/include/aux-platform.h new file mode 100644 index 0000000..63490e5 --- /dev/null +++ b/include/aux-platform.h @@ -0,0 +1,50 @@ +#ifndef __aux_platform_h__ +#define __aux_platform_h__ + +/* + * Terra Informatica Sciter and HTMLayout Engines + * http://terrainformatica.com/sciter, http://terrainformatica.com/htmlayout + * + * platform primitives. + * + * The code and information provided "as-is" without + * warranty of any kind, either expressed or implied. + * + * (C) 2003-2015, Andrew Fedoniouk (andrew@terrainformatica.com) + */ + + +#if defined(WIN64) || defined(_WIN64) || defined(_M_X64) + #define WINDOWS + #define X64BITS +#elif defined(WIN32) || defined(_WIN32) + #define WINDOWS +#elif defined(__APPLE__) + #define OSX + #define UTF8_CHARS // const char* is UTF8 sequence + #ifdef __x86_64__ + #define X64BITS + #endif + #define POSIX +#elif defined( __linux__ ) + #ifndef LINUX + #define LINUX + #endif + #ifdef __x86_64__ + #define X64BITS + #endif + #define POSIX + #define UTF8_CHARS // const char* is UTF8 sequence +#else + #error "Unknown platform" +#endif + +#if defined(WINDOWS) + #define stricmp _stricmp + #define wcsicmp _wcsicmp +#elif defined(POSIX) + #define stricmp strcasecmp + #define wcsicmp wcscasecmp +#endif + +#endif diff --git a/include/aux-slice.h b/include/aux-slice.h new file mode 100644 index 0000000..992e544 --- /dev/null +++ b/include/aux-slice.h @@ -0,0 +1,542 @@ +#ifndef __aux_slice_h__ +#define __aux_slice_h__ + +#if defined(__cplusplus) && !defined( PLAIN_API_ONLY ) + +/* + * Terra Informatica Sciter and HTMLayout Engines + * http://terrainformatica.com/sciter, http://terrainformatica.com/htmlayout + * + * slice - range of elements, start/length. That is what is known in D as array. + * + * The code and information provided "as-is" without + * warranty of any kind, either expressed or implied. + * + * (C) 2003-2015, Andrew Fedoniouk (andrew@terrainformatica.com) + */ + +/**\file + * \brief range of elements + **/ + +#include +#include "limits.h" +#include +#include "sciter-x-primitives.h" + +namespace aux +{ + + inline bool is_space( char c ) { return isspace(c & 0xff) != 0; } + inline bool is_space( WCHAR c ) { return iswspace(c) != 0; } + + inline bool is_digit( char c ) { return isdigit(c & 0xff) != 0; } + inline bool is_digit( WCHAR c ) { return iswdigit(c) != 0; } + + inline bool is_xdigit( char c ) { return isdigit(c & 0xff) != 0; } + inline bool is_xdigit( WCHAR c ) { return iswxdigit(c) != 0; } + + inline bool is_alpha( char c ) { return isalpha(c & 0xff) != 0; } + inline bool is_alpha( WCHAR c ) { return iswalpha(c) != 0; } + + inline bool is_alnum( char c ) { return isalnum(c & 0xff) != 0; } + inline bool is_alnum( WCHAR c ) { return iswalnum(c) != 0; } + + template + inline size_t wcslen( const CT* s ) { const CT *p = s; while (*p) p++; return p - s; } + +template + struct slice + { + const T* start; + size_t length; + + slice(): start(0), length(0) {} + slice(const T* start_, size_t length_) { start = start_; length = length_; } + slice(const slice& src): start(src.start), length(src.length) {} + + slice& operator = (const slice& src) { start = src.start; length = src.length; return *this; } + + // definitions to support for(auto a : slice) {} loops in C++11 + const T* begin() const { return start; } + const T* end() const { return start + length; } + + bool operator == ( const slice& r ) const + { + if( length != r.length ) + return false; + if( start == r.start ) + return true; + for( unsigned int i = 0; i < length; ++i ) + if( start[i] != r.start[i] ) + return false; + return true; + } + + bool operator != ( const slice& r ) const { return !operator==(r); } + + T operator[] ( unsigned int idx ) const + { + assert( idx < length ); + if(idx < length) + return start[idx]; + return 0; + } + + T last() const + { + if(length) + return start[length-1]; + return 0; + } + + // [idx1..length) + slice operator() ( unsigned int idx1 ) const + { + assert( idx1 < length ); + if ( idx1 < length ) + return slice( start + idx1, length - idx1 ); + return slice(); + } + // [idx1..idx2) + slice operator() ( unsigned int idx1, unsigned int idx2 ) const + { + assert( idx1 < length ); + assert( idx2 <= length ); + assert( idx1 <= idx2 ); + if ( idx1 < idx2 ) + return slice( start + idx1, idx2 - idx1 ); + return slice(); + } + + int index_of( T e ) const + { + for( unsigned int i = 0; i < length; ++i ) if( start[i] == e ) return i; + return -1; + } + + int last_index_of( T e ) const + { + for( size_t i = length; i > 0 ;) if( start[--i] == e ) return int(i); + return -1; + } + + int index_of( const slice& s ) const + { + if( s.length > length ) return -1; + if( s.length == 0 ) return -1; + size_t l = length - s.length; + for( unsigned int i = 0; i < l ; ++i) + if( start[i] == *s.start ) + { + const T* p = s.start; + size_t last = i + s.length; + for( unsigned int j = i + 1; j < last; ++j ) + if( *(++p) != start[j]) + goto next_i; + return i; + next_i: continue; + } + return -1; + } + + int last_index_of( const slice& s ) const + { + if( s.length > length ) return -1; + if( s.length == 0 ) return -1; + const T* ps = s.end() - 1; + for( size_t i = length; i > 0 ; ) + if( start[--i] == *ps ) + { + const T* p = ps; + size_t j, first = i - s.length + 1; + for( j = i; j > first; ) + if( *(--p) != start[--j]) + goto next_i; + return int(j); + next_i: continue; + } + return -1; + } + + template + bool starts_with(const slice &s) const { + if (length < s.length) + return false; + slice t(start, s.length); + return t == s; + } + + bool ends_with(const slice &s) const { + if (length < s.length) + return false; + slice t(start + length - s.length, s.length); + return t == s; + } + + void prune(size_t from_start, size_t from_end = 0) + { + size_t s = from_start >= length? length : from_start; + size_t e = length - (from_end >= length? length: from_end); + start += s; + if( s < e ) length = e-s; + else length = 0; + } + + bool split(const slice &delimeter, slice &head, slice &tail) const { + int d = index_of(delimeter); + if (d < 0) + return false; + const T *s = start; + size_t l = length; + head = slice(s, d); + tail = slice(s + d + delimeter.length, l - d - delimeter.length); + return true; + } + + +#ifdef CPP11 + explicit +#endif + operator bool() const { return length > 0; } + + T operator *() const { return length ? start[0] : T(); } + + T operator++() { if (length) { ++start; if (--length) return *start; } return T(); } + T operator++(int) { if (length) { --length; ++start; return *(start - 1); } return T(); } + + bool like(const T* pattern) const; + + }; + + #define MAKE_SLICE( T, D ) slice(D, sizeof(D) / sizeof(D[0])) + + // gets (static) array of elements and constructs slice of it (start/length pair) + template + inline slice make_slice(const T(&arr)[size]) { return slice(&arr[0], size); } + + #ifdef _DEBUG + + inline void slice_unittest() + { + int v1[] = { 0,1,2,3,4,5,6,7,8,9 }; + int v2[] = { 3,4,5 }; + int v3[] = { 0,1,2 }; + int v4[] = { 0,1,2,4 }; + int v5[] = { 1,1,2,3 }; + + slice s1 = MAKE_SLICE( int, v1 ); + slice s2 = MAKE_SLICE( int, v2 ); + slice s3 = MAKE_SLICE( int, v3 ); + slice s4 = MAKE_SLICE( int, v4 ); + slice s5 = MAKE_SLICE( int, v5 ); + + assert( s1 != s2 ); + assert( s1(3,6) == s2 ); + assert( s1.index_of(3) == 3 ); + assert( s1.index_of(s2) == 3 ); + assert( s1.last_index_of(3) == 3 ); + assert( s1.last_index_of(s2) == 3 ); + + assert( s1.index_of(s3) == 0 ); + assert( s1.last_index_of(s3) == 0 ); + + assert( s1.index_of(s4) == -1 ); + assert( s1.last_index_of(s4) == -1 ); + + assert( s1.index_of(s5) == -1 ); + assert( s1.last_index_of(s5) == -1 ); + + } + + #endif + + template + inline slice trim_left(slice str) + { + for( unsigned i = 0; i < str.length; ++i ) + if( is_space(str[0]) ) { ++str.start; --str.length; } + else break; + return str; + } + + template + inline slice trim_right(slice str) + { + for( int j = int(str.length) - 1; j >= 0; --j ) + if( is_space(str[j]) ) --str.length; + else break; + return str; + } + + template + inline slice trim(slice str) + { + return trim_right(trim_left(str)); + } + + typedef slice chars; + typedef slice wchars; + typedef slice bytes; + + + // Note: CS here is a string literal! + #define const_chars(CS) aux::slice(CS,CHARS_IN(CS)) + #define const_wchars(CS) aux::slice(WSTR(CS),CHARS_IN(_WSTR(CS))) + + inline wchars chars_of( const WCHAR *t ) { return t? wchars(t,(unsigned int)wcslen(t)):wchars(); } + inline chars chars_of( const char *t ) { return t? chars(t,(unsigned int)strlen(t)):chars(); } + + + + template + slice chars_of( const std::basic_string &s ) { return slice(s.c_str(), s.length()); } + + template + slice elements_of(const T* s, size_t l) { return slice(s, l); } + + template + slice elements_of( const std::vector &s ) { return slice(s.data(), s.size()); } + + template + slice elements_of(T (& arr)[size]){ return slice(&arr[0],size);} + + template + slice elements_of(const T (& arr)[size]){ return slice(&arr[0],size);} + + template + std::basic_string make_string( aux::slice s ) { return std::basic_string(s.start, s.length); } + + // simple tokenizer + template + class tokens + { + const T* delimeters; + const T* p; + const T* tail; + const T* start; + const T* end; + const bool is_delimeter(T el) { for(const T* t = delimeters;t && *t; ++t) if(el == *t) return true; return false; } + const T* tok() { for(;p < tail; ++p) if(is_delimeter(*p)) return p++; return p; } + public: + + tokens(const T *text, size_t text_length, const T* separators): delimeters(separators) + { + start = p = text; + tail = p + text_length; + end = tok(); + } + + tokens(const aux::slice s, const T* separators): delimeters(separators) + { + start = p = s.start; + tail = p + s.length; + end = tok(); + } + + bool next(slice& v) + { + if(start < tail) + { + v.start = start; + v.length = unsigned(end - start); + start = p; + end = tok(); + return true; + } + return false; + } + }; + + typedef tokens atokens; + typedef tokens wtokens; + + + /****************************************************************************/ + // + // idea was taken from Konstantin Knizhnik's FastDB + // see http://www.garret.ru/ + // extended by [] operations + // + + template + struct charset + { + #define SET_SIZE (1 << (sizeof(CT) * CHAR_BIT)) + #define SIGNIFICANT_BITS_MASK unsigned( SET_SIZE - 1 ) + + unsigned char codes[ SET_SIZE / CHAR_BIT ]; + + unsigned charcode(CT c) // proper unsigned_cast + { + return SIGNIFICANT_BITS_MASK & unsigned(c); + } + + private: + void set ( CT from, CT to, bool v ) + { + for ( unsigned i = charcode(from); i <= charcode(to); ++i ) + { + unsigned int bit = i & 7; + unsigned int octet = i >> 3; + if( v ) codes[octet] |= 1 << bit; else codes[octet] &= ~(1 << bit); + } + } + void init ( unsigned char v ) { memset(codes,v,(SET_SIZE >> 3)); } + public: + + void parse ( const CT* &pp ) + { + //assert( sizeof(codes) == sizeof(CT) * sizeof(bool)); + const CT *p = (const CT *) pp; + unsigned char inv = *p == '^'? 0xff:0; + if ( inv ) { ++p; } + init ( inv ); + if ( *p == sep ) set(sep,sep,inv == 0); + while ( *p ) + { + if ( p[0] == end ) { p++; break; } + if ( p[1] == sep && p[2] != 0 ) { set (p[0], p[2], inv == 0 ); p += 3; } + else { CT t = *p++; set(t,t, inv == 0); } + } + pp = (const CT *) p; + } + + bool valid ( CT c ) + { + unsigned int bit = charcode(c) & 7; + unsigned int octet = charcode(c) >> 3; + return (codes[octet] & (1 << bit)) != 0; + } + #undef SET_SIZE + #undef SIGNIFICANT_BITS_MASK + }; + + template + inline int match ( slice cr, const CT *pattern ) + { + const CT AnySubstring = '*'; + const CT AnyOneChar = '?'; + const CT AnyOneDigit = '#'; + + const CT *str = cr.start; + const CT *wildcard = 0; + const CT *strpos = 0; + const CT *matchpos = 0; + + charset cset; + + for (;;) + { + if ( *pattern == AnySubstring ) + { + wildcard = ++pattern; + strpos = str; + if ( !matchpos ) matchpos = str; + } + else if ( *str == '\0' || str >= cr.end() ) + { + return ( *pattern == '\0' ) ? int( matchpos - cr.start ) : -1; + } + else if ( *pattern == '[' ) + { + pattern++; + cset.parse ( pattern ); + if ( !cset.valid ( *str ) ) + return -1; + if ( !matchpos ) + matchpos = str; + str += 1; + } + else if ( *str == *pattern || *pattern == AnyOneChar ) + { + if ( !matchpos ) matchpos = str; + str += 1; + pattern += 1; + } + else if (*pattern == AnyOneDigit) + { + if (isdigit(*str)) { + if (!matchpos) matchpos = str; + str++; + pattern++; + } + else if (wildcard) { + str = ++strpos; + pattern = wildcard; + } + else + return -1; + } + else if ( wildcard ) + { + str = ++strpos; + pattern = wildcard; + } + else + break; + } + return -1; + } + + template + inline bool slice::like ( const T *pattern ) const + { + return match(*this,pattern) >= 0; + } + + // chars to unsigned int + // chars to int + + template + inline unsigned int to_uint(slice& span, unsigned int base = 10) + { + unsigned int result = 0,value; + const T *cp = span.start; + const T *pend = span.end(); + + while ( cp < pend && is_space(*cp) ) ++cp; + + if (!base) + { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((toupper(*cp) == 'X') && is_xdigit(cp[1])) { + cp++; + base = 16; + } + } + } + else if (base == 16) + { + if (cp[0] == '0' && toupper(cp[1]) == 'X') + cp += 2; + } + while ( cp < pend && is_xdigit(*cp) && + (value = is_digit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + span.length = (unsigned int)(cp - span.start); + return result; + } + + template + int to_int(slice& span, unsigned int base = 10) + { + + while (span.length > 0 && is_space(span[0]) ) { ++span.start; --span.length; } + if(span[0] == '-') + { + ++span.start; --span.length; + return - int(to_uint(span,base)); + } + return to_uint(span,base); + } + +} + +#endif +#endif diff --git a/include/behaviors/behavior_camera_capture.cpp b/include/behaviors/behavior_camera_capture.cpp new file mode 100644 index 0000000..929d3f0 --- /dev/null +++ b/include/behaviors/behavior_camera_capture.cpp @@ -0,0 +1,127 @@ + +#include "sciter-x.h" + +#if TRUE // change it to FALSE to disable camera functionality + +#include +#include +#include "camera/camera-capture.h" +#include "camera/camera-capture.cpp" + +namespace sciter +{ +/* + +BEHAVIOR: camera_stream + provides video frames from camera +COMMENTS: +