Skip to content

Instantly share code, notes, and snippets.

@steveblue
Created January 29, 2018 06:26
Show Gist options
  • Select an option

  • Save steveblue/4469b224e7d69dd67232b6af5860d669 to your computer and use it in GitHub Desktop.

Select an option

Save steveblue/4469b224e7d69dd67232b6af5860d669 to your computer and use it in GitHub Desktop.
A Service for Angular to detect Responsive states, user agent, and to create a visual grid with JavaScript
export interface IGrid {
col: number[],
colSpan: number[],
cols: number,
gutter: number,
margin: number,
width: number,
}
export interface IResStates {
[key: string]: number[]
}
import { IResStates } from '../../interfaces/res.interface';
import { IGrid } from '../../interfaces/grid.interface';
import { EventEmitter, Injectable } from '@angular/core';
export class Res {
uagent: string;
state: string;
input: string;
orient: string;
device: string;
os: string;
browser: string;
version: string;
width: number;
grid: IGrid;
viewports: IResStates | any;
gridsettings: IResStates | any;
columnWidth: number;
emitter: EventEmitter<Res>;
constructor(json: any) {
this.uagent = navigator.userAgent.toLowerCase();
this.state = undefined;
this.input = undefined;
this.orient = undefined;
this.device = undefined;
this.os = undefined;
this.browser = undefined;
this.version = undefined;
this.width = 0;
this.grid;
this.viewports = {};
this.gridsettings = {};
this.emitter = new EventEmitter();
let lastBreakpoint = 0;
for (var i = 0; i < json.length; i++) {
this.viewports[json[i].state] = [lastBreakpoint + 1, json[i].breakpoint];
if (json[i].cols !== undefined && json[i].margin !== undefined && json[i].gutter !== undefined) {
this.gridsettings[json[i].state] = [json[i].cols, json[i].margin, json[i].gutter];
}
lastBreakpoint = json[i].breakpoint;
};
this.init();
}
setState() {
return new Promise((res) => {
if (this.device === 'desktop') {
this.width = window.innerWidth;
}
else if (this.device !== 'desktop') {
if (this.orient === 'portrait') {
this.width = screen.width;
} else if (this.orient === 'landscape') {
this.width = screen.height;
}
}
for (var key in this.viewports) {
if (this.viewports.hasOwnProperty(key)) {
if (this.width >= this.viewports[key][0] && this.width <= this.viewports[key][1]) {
this.state = key;
res();
//return this.state;
}
}
}
});
}
inputCheck() {
if (this.os === 'ios' || this.os === 'android' || this.os === 'winphone') {
this.input = 'touch';
} else {
this.input = 'mouse';
}
}
browserCheck() {
var tem,
M = this.uagent.match(/(edge|opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
if (this.uagent.match(/(edge(?=\/))\/?\s*(\d+)/i)) {
M = this.uagent.match(/(edge(?=\/))\/?\s*(\d+)/i);
this.browser = 'edge';
this.version = M[2];
return 'Edge ' + (M[2] || '');
}
if (/trident/i.test(M[1])) {
tem = /\brv[ :]+(\d+)/g.exec(this.uagent) || [];
this.browser = 'msie';
this.version = tem[1];
return 'IE ' + (tem[1] || '');
}
if (M[1] === 'Chrome') {
tem = this.uagent.match(/\bOPR\/(\d+)/);
if (tem != null) {
this.browser = 'opera';
this.version = tem[1];
return 'Opera ' + tem[1];
}
}
M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?'];
if ((tem = this.uagent.match(/version\/(\d+)/i)) != null) {
M.splice(1, 1, tem[1]);
}
this.browser = M[0];
this.version = M[1];
return M.join(' ');
}
osCheck() {
if (navigator.appVersion.indexOf("Win") != -1) {
this.os = 'windows';
this.device = 'desktop';
} else if (navigator.appVersion.indexOf("Mac") != -1 && navigator.userAgent.match(/(iPhone|iPod|iPad)/) == null) {
this.os = 'macos';
this.device = 'desktop';
} else if (navigator.userAgent.indexOf("Android") > -1) {
this.os = 'android';
if (navigator.userAgent.indexOf("Mobile") > -1) {
this.device = 'mobile';
} else {
this.device = 'tablet';
}
} else if (navigator.userAgent.indexOf("windows phone") > 0) {
this.os = 'windows';
this.device = 'mobile';
} else if (navigator.appVersion.indexOf("X11") != -1) {
this.os = 'unix';
this.device = 'desktop';
} else if (navigator.appVersion.indexOf("Linux") != -1) {
this.os = 'linux';
this.device = 'desktop';
} else if (navigator.userAgent.match(/(iPhone|iPod|iPad)/) !== null && navigator.userAgent.match(/(iPhone|iPod|iPad)/).length > 0) {
this.os = 'ios';
if (this.uagent.indexOf("iphone") > 0) {
this.device = "iphone";
}
if (this.uagent.indexOf("ipod") > 0) {
this.device = "ipod";
}
if (this.uagent.indexOf("ipad") > 0) {
this.device = "ipad";
}
} else {
this.os = 'unknown';
}
}
gridHelper(key: string) {
let col,
colArr = [],
colSpan,
colSpanArr = [],
margin,
gutter,
cols;
cols = this.gridsettings[key][0];
margin = this.gridsettings[key][1];
gutter = this.gridsettings[key][2];
col = [];
colSpan = [];
this.width = window.innerWidth - (margin * 2) + gutter;
this.columnWidth = (this.width / cols) - gutter;
for (var i = 0; i < cols; i++) {
if (i === 0) {
colSpan = 0;
} else {
colSpan = (this.columnWidth * i) + (gutter * (i - 1));
}
col = ((this.width / cols) * i) + margin;
colArr.push(col);
colSpanArr.push(colSpan);
if (i === cols - 1) {
colSpan = (this.columnWidth * (i + 1)) + (gutter * (i))
colSpanArr.push(colSpan);
}
}
return {
"cols": cols,
"col": colArr,
"colSpan": colSpanArr,
"width": this.width,
"margin": margin,
"gutter": gutter
};
}
hasClass(ele: Element, cls: string) {
return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
}
addClass(ele: Element, cls: string) {
if (!this.hasClass(ele, cls)) ele.className += " " + cls;
}
removeClass(ele: Element, cls: string) {
if (this.hasClass(ele, cls)) {
var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
ele.className = ele.className.replace(reg, ' ');
}
}
handleClasses(elem: Element, res: Res) {
let model;
if (res) {
model = res;
} else {
model = this;
}
for (var key in model.viewports) {
this.removeClass(elem, 'is--' + key);
}
this.addClass(elem, 'is--' + model.state);
this.removeClass(elem, 'is--portrait');
this.removeClass(elem, 'is--landscape');
this.addClass(elem, 'is--' + model.orient);
if (!this.addClass(elem, 'is--' + model.os)) {
this.addClass(elem, 'is--' + model.os);
}
if (!this.addClass(elem, 'is--' + model.browser)) {
this.addClass(elem, 'is--' + model.browser);
}
if (!this.addClass(elem, 'is--' + model.browser + '-' + model.version)) {
this.addClass(elem, 'is--' + model.browser + '-' + model.version);
}
}
handleBodyClasses() {
this.handleClasses(document.body, null);
}
resize() {
if (window.innerHeight > window.innerWidth) {
this.orient = 'portrait';
} else {
this.orient = 'landscape';
}
this.setState().then(() => {
if (this.gridsettings.hasOwnProperty(this.state)) {
this.grid = this.gridHelper(this.state);
}
this.handleBodyClasses();
this.emitter.emit(this);
});
}
init() {
let that = this;
this.osCheck();
this.inputCheck();
this.browserCheck();
window.onorientationchange = () => {
this.resize();
};
window.onresize = () => {
this.resize();
};
this.resize();
}
}
@Injectable()
export class ResService {
model: Res;
emitter: EventEmitter<Res>;
handleClasses: Function;
hasClass: Function;
removeClass: Function;
addClass: Function;
constructor() {
this.model = new Res([{
"state": "mobile",
"breakpoint": 720,
"cols": 4,
"margin": 0,
"gutter": 10
},
{
"state": "tablet",
"breakpoint": 1200,
"cols": 12,
"margin": 0,
"gutter": 20
},
{
"state": "desktop",
"breakpoint": 1480,
"cols": 12,
"margin": 0,
"gutter": 30
},
{
"state": "hd",
"breakpoint": 9999,
"cols": 12,
"margin": 0,
"gutter": 30
}]);
this.handleClasses = this.model.handleClasses;
this.addClass = this.model.addClass;
this.removeClass = this.model.removeClass;
this.hasClass = this.model.hasClass;
this.emitter = this.model.emitter;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment