diff --git a/core/modules/info/bowser/bowser.js b/core/modules/info/bowser/bowser.js new file mode 100644 index 000000000..3b4c03de4 --- /dev/null +++ b/core/modules/info/bowser/bowser.js @@ -0,0 +1,239 @@ +/*! + * Bowser - a browser detector + * https://github.com/ded/bowser + * MIT License | (c) Dustin Diaz 2014 + */ + +!function (name, definition) { + if (typeof module != 'undefined' && module.exports) module.exports['browser'] = definition() + else if (typeof define == 'function') define(definition) + else this[name] = definition() +}('bowser', function () { + /** + * See useragents.js for examples of navigator.userAgent + */ + + var t = true + + function detect(ua) { + + function getFirstMatch(regex) { + var match = ua.match(regex); + return (match && match.length > 1 && match[1]) || ''; + } + + var iosdevice = getFirstMatch(/(ipod|iphone|ipad)/i).toLowerCase() + , likeAndroid = /like android/i.test(ua) + , android = !likeAndroid && /android/i.test(ua) + , versionIdentifier = getFirstMatch(/version\/(\d+(\.\d+)?)/i) + , tablet = /tablet/i.test(ua) + , mobile = !tablet && /[^-]mobi/i.test(ua) + , result + + if (/opera|opr/i.test(ua)) { + result = { + name: 'Opera' + , opera: t + , version: versionIdentifier || getFirstMatch(/(?:opera|opr)[\s\/](\d+(\.\d+)?)/i) + } + } + else if (/windows phone/i.test(ua)) { + result = { + name: 'Windows Phone' + , windowsphone: t + , msie: t + , version: getFirstMatch(/iemobile\/(\d+(\.\d+)?)/i) + } + } + else if (/msie|trident/i.test(ua)) { + result = { + name: 'Internet Explorer' + , msie: t + , version: getFirstMatch(/(?:msie |rv:)(\d+(\.\d+)?)/i) + } + } + else if (/chrome|crios|crmo/i.test(ua)) { + result = { + name: 'Chrome' + , chrome: t + , version: getFirstMatch(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i) + } + } + else if (iosdevice) { + result = { + name : iosdevice == 'iphone' ? 'iPhone' : iosdevice == 'ipad' ? 'iPad' : 'iPod' + } + // WTF: version is not part of user agent in web apps + if (versionIdentifier) { + result.version = versionIdentifier + } + } + else if (/sailfish/i.test(ua)) { + result = { + name: 'Sailfish' + , sailfish: t + , version: getFirstMatch(/sailfish\s?browser\/(\d+(\.\d+)?)/i) + } + } + else if (/seamonkey\//i.test(ua)) { + result = { + name: 'SeaMonkey' + , seamonkey: t + , version: getFirstMatch(/seamonkey\/(\d+(\.\d+)?)/i) + } + } + else if (/firefox|iceweasel/i.test(ua)) { + result = { + name: 'Firefox' + , firefox: t + , version: getFirstMatch(/(?:firefox|iceweasel)[ \/](\d+(\.\d+)?)/i) + } + if (/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(ua)) { + result.firefoxos = t + } + } + else if (/silk/i.test(ua)) { + result = { + name: 'Amazon Silk' + , silk: t + , version : getFirstMatch(/silk\/(\d+(\.\d+)?)/i) + } + } + else if (android) { + result = { + name: 'Android' + , version: versionIdentifier + } + } + else if (/phantom/i.test(ua)) { + result = { + name: 'PhantomJS' + , phantom: t + , version: getFirstMatch(/phantomjs\/(\d+(\.\d+)?)/i) + } + } + else if (/blackberry|\bbb\d+/i.test(ua) || /rim\stablet/i.test(ua)) { + result = { + name: 'BlackBerry' + , blackberry: t + , version: versionIdentifier || getFirstMatch(/blackberry[\d]+\/(\d+(\.\d+)?)/i) + } + } + else if (/(web|hpw)os/i.test(ua)) { + result = { + name: 'WebOS' + , webos: t + , version: versionIdentifier || getFirstMatch(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i) + }; + /touchpad\//i.test(ua) && (result.touchpad = t) + } + else if (/bada/i.test(ua)) { + result = { + name: 'Bada' + , bada: t + , version: getFirstMatch(/dolfin\/(\d+(\.\d+)?)/i) + }; + } + else if (/tizen/i.test(ua)) { + result = { + name: 'Tizen' + , tizen: t + , version: getFirstMatch(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i) || versionIdentifier + }; + } + else if (/safari/i.test(ua)) { + result = { + name: 'Safari' + , safari: t + , version: versionIdentifier + } + } + else result = {} + + // set webkit or gecko flag for browsers based on these engines + if (/(apple)?webkit/i.test(ua)) { + result.name = result.name || "Webkit" + result.webkit = t + if (!result.version && versionIdentifier) { + result.version = versionIdentifier + } + } else if (!result.opera && /gecko\//i.test(ua)) { + result.name = result.name || "Gecko" + result.gecko = t + result.version = result.version || getFirstMatch(/gecko\/(\d+(\.\d+)?)/i) + } + + // set OS flags for platforms that have multiple browsers + if (android || result.silk) { + result.android = t + } else if (iosdevice) { + result[iosdevice] = t + result.ios = t + } + + // OS version extraction + var osVersion = ''; + if (iosdevice) { + osVersion = getFirstMatch(/os (\d+([_\s]\d+)*) like mac os x/i); + osVersion = osVersion.replace(/[_\s]/g, '.'); + } else if (android) { + osVersion = getFirstMatch(/android[ \/-](\d+(\.\d+)*)/i); + } else if (result.windowsphone) { + osVersion = getFirstMatch(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i); + } else if (result.webos) { + osVersion = getFirstMatch(/(?:web|hpw)os\/(\d+(\.\d+)*)/i); + } else if (result.blackberry) { + osVersion = getFirstMatch(/rim\stablet\sos\s(\d+(\.\d+)*)/i); + } else if (result.bada) { + osVersion = getFirstMatch(/bada\/(\d+(\.\d+)*)/i); + } else if (result.tizen) { + osVersion = getFirstMatch(/tizen[\/\s](\d+(\.\d+)*)/i); + } + if (osVersion) { + result.osversion = osVersion; + } + + // device type extraction + var osMajorVersion = osVersion.split('.')[0]; + if (tablet || iosdevice == 'ipad' || (android && (osMajorVersion == 3 || (osMajorVersion == 4 && !mobile))) || result.silk) { + result.tablet = t + } else if (mobile || iosdevice == 'iphone' || iosdevice == 'ipod' || android || result.blackberry || result.webos || result.bada) { + result.mobile = t + } + + // Graded Browser Support + // http://developer.yahoo.com/yui/articles/gbs + if ((result.msie && result.version >= 10) || + (result.chrome && result.version >= 20) || + (result.firefox && result.version >= 20.0) || + (result.safari && result.version >= 6) || + (result.opera && result.version >= 10.0) || + (result.ios && result.osversion && result.osversion.split(".")[0] >= 6) + ) { + result.a = t; + } + else if ((result.msie && result.version < 10) || + (result.chrome && result.version < 20) || + (result.firefox && result.version < 20.0) || + (result.safari && result.version < 6) || + (result.opera && result.version < 10.0) || + (result.ios && result.osversion && result.osversion.split(".")[0] < 6) + ) { + result.c = t + } else result.x = t + + return result + } + + var bowser = detect(typeof navigator !== 'undefined' ? navigator.userAgent : '') + + + /* + * Set our detect method to the main bowser object so we can + * reuse it to test other user agents. + * This is needed to implement future tests. + */ + bowser._detect = detect; + + return bowser +}); diff --git a/core/modules/info/bowser/tiddlywiki.files b/core/modules/info/bowser/tiddlywiki.files new file mode 100644 index 000000000..4db95c675 --- /dev/null +++ b/core/modules/info/bowser/tiddlywiki.files @@ -0,0 +1,12 @@ +{ + "tiddlers": [ + { + "file": "bowser.js", + "fields": { + "type": "application/javascript", + "title": "$:/core/modules/info/bowser/bowser.js", + "module-type": "library" + } + } + ] +} diff --git a/core/modules/info/browser.js b/core/modules/info/browser.js new file mode 100644 index 000000000..223762fd6 --- /dev/null +++ b/core/modules/info/browser.js @@ -0,0 +1,79 @@ +/*\ +title: $:/core/modules/info/browser.js +type: application/javascript +module-type: info + +Initialise $:/info/browser tiddlers + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.getInfoTiddlerFields = function() { + var mapBoolean = function(value) {return value ? "yes" : "no"}, + infoTiddlerFields = []; + // Basics + infoTiddlerFields.push({title: "$:/info/browser", text: mapBoolean(!!$tw.browser)}); + if($tw.browser) { + // Mappings from tiddler titles (prefixed with "$:/info/browser/") to bowser.browser property name + var bowser = require("$:/core/modules/info/bowser/bowser.js"), + mappings = [ + ["name","name","unknown"], + ["version","version"], + ["is/webkit","webkit"], + ["is/gecko","gecko"], + ["is/chrome","chrome"], + ["is/firefox","firefox"], + ["is/ios","ios"], + ["is/iphone","iphone"], + ["is/ipad","ipad"], + ["is/ipod","ios"], + ["is/opera","opera"], + ["is/phantomjs","phantomjs"], + ["is/safari","safari"], + ["is/seamonkey","seamonkey"], + ["is/blackberry","blackberry"], + ["is/webos","webos"], + ["is/silk","silk"], + ["is/bada","bada"], + ["is/tizen","tizen"], + ["is/sailfish","sailfish"], + ["is/android","android"], + ["is/windowsphone","windowsphone"], + ["is/firefoxos","firefoxos"] + ]; + $tw.utils.each(mappings,function(mapping) { + var value = bowser.browser[mapping[1]]; + if(value === undefined) { + value = mapping[2]; + } + if(value === undefined) { + value = false; + } + if(typeof value === "boolean") { + value = mapBoolean(value); + } + infoTiddlerFields.push({title: "$:/info/browser/" + mapping[0], text: value}); + }); + // Set $:/info/browser/name to the platform with some changes from Bowser + var platform = bowser.browser.name; + if("iPad iPhone iPod".split(" ").indexOf(platform) !== -1) { + platform = "iOS"; + } + infoTiddlerFields.push({title: "$:/info/browser/name", text: platform}); + // Non-bowser settings for TiddlyFox and TiddlyDesktop + var hasTiddlyFox = !!document.getElementById("tiddlyfox-message-box"), + isTiddlyDesktop = false; // Can't detect it until we update TiddlyDesktop to have a distinct useragent string + infoTiddlerFields.push({title: "$:/info/browser/has/tiddlyfox", text: mapBoolean(hasTiddlyFox)}); + infoTiddlerFields.push({title: "$:/info/browser/is/tiddlydesktop", text: mapBoolean(isTiddlyDesktop)}); + if(isTiddlyDesktop) { + infoTiddlerFields.push({title: "$:/info/browser/name", text: "TiddlyDesktop"}); + } + } + return infoTiddlerFields; +}; + +})(); diff --git a/core/modules/startup/info.js b/core/modules/startup/info.js new file mode 100644 index 000000000..1ebf568f4 --- /dev/null +++ b/core/modules/startup/info.js @@ -0,0 +1,48 @@ +/*\ +title: $:/core/modules/startup/info.js +type: application/javascript +module-type: startup + +Initialise $:/info tiddlers via $:/temp/info-plugin pseudo-plugin + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +// Export name and synchronous status +exports.name = "info"; +exports.before = ["startup"]; +exports.after = ["load-modules"]; +exports.synchronous = true; + +exports.startup = function() { + // Collect up the info tiddlers + var infoTiddlerFields = {}; + // Give each info module a chance to fill in as many info tiddlers as they want + $tw.modules.forEachModuleOfType("info",function(title,moduleExports) { + if(moduleExports && moduleExports.getInfoTiddlerFields) { + var tiddlerFieldsArray = moduleExports.getInfoTiddlerFields(infoTiddlerFields); + $tw.utils.each(tiddlerFieldsArray,function(fields) { + if(fields) { + infoTiddlerFields[fields.title] = fields; + } + }); + } + }); + // Bake the info tiddlers into a plugin + var fields = { + title: "$:/temp/info-plugin", + type: "application/json", + "plugin-type": "info", + text: JSON.stringify({tiddlers: infoTiddlerFields},null,$tw.config.preferences.jsonSpaces) + }; + $tw.wiki.addTiddler(new $tw.Tiddler(fields)); + $tw.wiki.readPluginInfo(); + $tw.wiki.registerPluginTiddlers("info"); + $tw.wiki.unpackPluginTiddlers(); +}; + +})(); diff --git a/editions/tw5.com-server/tiddlers/InfoMechanism.tid b/editions/tw5.com-server/tiddlers/InfoMechanism.tid new file mode 100644 index 000000000..769aad997 --- /dev/null +++ b/editions/tw5.com-server/tiddlers/InfoMechanism.tid @@ -0,0 +1,59 @@ +created: 20140720164948099 +modified: 20140720165248031 +tags: mechanism +title: InfoMechanism +type: text/vnd.tiddlywiki + +System tiddlers in the namespace `$:/info/` are used to expose information about the system (including the current browser) so that WikiText applications can adapt themselves to available features. + +! Information Tiddlers + +|!Title |!Description | +|[[$:/info/browser]] |Running in the browser? ("yes" or "no") | +|[[$:/info/browser/has/tiddlyfox]] |Is TiddlyFox available? ("yes" or "no") | +|[[$:/info/browser/is/android]] |Running on Android? ("yes" or "no") | +|[[$:/info/browser/is/bada]] |Running on Bada? ("yes" or "no") | +|[[$:/info/browser/is/blackberry]] |Running on Blackberry? ("yes" or "no") | +|[[$:/info/browser/is/chrome]] |Running on Chrome? ("yes" or "no") | +|[[$:/info/browser/is/firefox]] |Running on Firefox? ("yes" or "no") | +|[[$:/info/browser/is/firefoxos]] |Running on Firefox OS? ("yes" or "no") | +|[[$:/info/browser/is/gecko]] |Running on Gecko? ("yes" or "no") | +|[[$:/info/browser/is/ios]] |Running on iOS (ie an iPhone, iPad or iPod)? ("yes" or "no") | +|[[$:/info/browser/is/ipad]] |Running on iPad? ("yes" or "no") | +|[[$:/info/browser/is/iphone]] |Running on iPhone? ("yes" or "no") | +|[[$:/info/browser/is/ipod]] |Running on iPod? ("yes" or "no") | +|[[$:/info/browser/is/opera]] |Running on Opera? ("yes" or "no") | +|[[$:/info/browser/is/phantomjs]] |Running on PhantomJS? ("yes" or "no") | +|[[$:/info/browser/is/safari]] |Running on Safari? ("yes" or "no") | +|[[$:/info/browser/is/sailfish]] |Running on Sailfish? ("yes" or "no") | +|[[$:/info/browser/is/seamonkey]] |Running on Sea Monkey? ("yes" or "no") | +|[[$:/info/browser/is/silk]] |Running on Amazon's Silk? ("yes" or "no") | +|[[$:/info/browser/is/tiddlydesktop]] |Running on TiddlyDesktop? ("yes" or "no") | +|[[$:/info/browser/is/tizen]] |Running on Tizen? ("yes" or "no") | +|[[$:/info/browser/is/webkit]] |Running on WebKit? ("yes" or "no") | +|[[$:/info/browser/is/webos]] |Running on WebOS? ("yes" or "no") | +|[[$:/info/browser/is/windowsphone]] |Running on Windows Phone? ("yes" or "no") | +|[[$:/info/browser/name]] |Platform name (see below) | +|[[$:/info/browser/version]] |Browser version | + +The browser information is obtained with [[Bowser, a browser detector library from Dustin Diaz|https://github.com/ded/bowser/]]. Possible browser names include: + +* ''"Amazon Silk"'' +* ''"Android"'' +* ''"Bada"'' +* ''"BlackBerry"'' +* ''"Chrome"'' +* ''"Firefox"'' +* ''"Internet Explorer"'' +* ''"iOS"'' +* ''"Opera"'' +* ''"PhantomJS"'' +* ''"Safari"'' +* ''"Sailfish"'' +* ''"SeaMonkey"'' +* ''"TiddlyDesktop"'' +* ''"Tizen"'' +* ''"WebOS"'' +* ''"Windows Phone"'' + +Note that Bowser returns "iPhone", "iPad" and "iPod" as distinct values for the name of the current browser. TiddlyWiki converts all three distinct values into "iOS" before copying to [[$:/info/browser/name]].