How to work-around iOS fullscreen by minimizing URL bar - ios

Actually I am trying to implement the solution available here https://im2.ezgif.com/tmp/ezgif-2-2f480da723.gif.
The hand, after touching and making finger move, minimize UI and opens, let say, something similar to the fullscreen.
For some reason code below isn't working on some Safari devices – iPhone 12 pro, 13 and 14 work correctly, while iPhone XS and XR don't.
my TS:
merge(fromEvent(window, 'scroll'), fromEvent(window, 'resize'), fromEvent(window, 'orientationchange'), of(null))
.pipe(takeUntil(this.destroy$))
.subscribe((e) => {
if (window.innerHeight > window.innerWidth) {
this.isFullscreen = window.innerHeight > document.documentElement.clientHeight + 50;
} else {
this.isFullscreen = window.innerHeight === document.documentElement.clientHeight;
}
if (this.isFullscreen) {
document.querySelector('html').classList.add('is-fullscreen');
window.scrollTo(0, 500);
} else {
document.querySelector('html').classList.remove('is-fullscreen');
document.querySelector('html').classList.remove('ios-scroll');
}
});
ngAfterViewInit() {
const elem = this.elemRef.nativeElement.querySelector('.ios-wrapper');
if (elem) {
elem.addEventListener('gesturestart', (e) => {
e.preventDefault();
});
elem.addEventListener('gestureend', (e) => {
e.preventDefault();
});
elem.addEventListener('gesturechange', (e) => {
e.preventDefault();
});
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
document.querySelector('html').classList.remove('ios-scroll');
}
HTML:
<div class="ios-wrapper" *ngIf="access" [ngClass]="{ 'is-fullscreen': isFullscreen }">
<div class="ios-hand">
<i class="far fa-hand-pointer"></i>
</div>
</div>
SCSS:
.ios-wrapper {
visibility: visible;
opacity: 1;
width: 100%;
height: 1000000px;
z-index: 9999;
position: absolute;
top: -500px;
left: 0;
margin: 0;
padding: 0;
background: rgba(0,0,0,0.5);
&.is-fullscreen {
visibility: hidden !important;
opacity: 0 !important;
z-index: 0;
pointer-events: none;
}
}
My purpose is to implement the solution which is working on every Safari device. Do you know where is the mistake or why it's not working on some iPhones?

Related

How can I Integrate this code in .html file and .ts file?

I want to drag and drop functionality using this below URL into .html(which contains the drag and drop element) and .ts file(file contains the backend part)
https://www.npmjs.com/package/material-ui-dropzone
You don't need to use an npm package for this check out my example here
// ************************ Drag and drop ***************** //
let dropArea = document.getElementById("drop-area")
// Prevent default drag behaviors
;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, preventDefaults, false)
document.body.addEventListener(eventName, preventDefaults, false)
})
// Highlight drop area when item is dragged over it
;['dragenter', 'dragover'].forEach(eventName => {
dropArea.addEventListener(eventName, highlight, false)
})
;['dragleave', 'drop'].forEach(eventName => {
dropArea.addEventListener(eventName, unhighlight, false)
})
// Handle dropped files
dropArea.addEventListener('drop', handleDrop, false)
function preventDefaults (e) {
e.preventDefault()
e.stopPropagation()
}
function highlight(e) {
dropArea.classList.add('highlight')
}
function unhighlight(e) {
dropArea.classList.remove('active')
}
function handleDrop(e) {
var dt = e.dataTransfer
var files = dt.files
handleFiles(files)
}
let uploadProgress = []
let progressBar = document.getElementById('progress-bar')
function initializeProgress(numFiles) {
progressBar.value = 0
uploadProgress = []
for(let i = numFiles; i > 0; i--) {
uploadProgress.push(0)
}
}
function updateProgress(fileNumber, percent) {
uploadProgress[fileNumber] = percent
let total = uploadProgress.reduce((tot, curr) => tot + curr, 0) / uploadProgress.length
console.debug('update', fileNumber, percent, total)
progressBar.value = total
}
function handleFiles(files) {
files = [...files]
initializeProgress(files.length)
files.forEach(uploadFile)
files.forEach(previewFile)
}
function previewFile(file) {
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onloadend = function() {
let img = document.createElement('img')
img.src = reader.result
document.getElementById('gallery').appendChild(img)
}
}
function uploadFile(file, i) {
var url = 'the service url to upload the file'
var xhr = new XMLHttpRequest()
var formData = new FormData()
xhr.open('POST', url, true)
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
// Update progress (can be used to show progress indicator)
xhr.upload.addEventListener("progress", function(e) {
updateProgress(i, (e.loaded * 100.0 / e.total) || 100)
})
xhr.addEventListener('readystatechange', function(e) {
if (xhr.readyState == 4 && xhr.status == 200) {
updateProgress(i, 100) // <- Add this
}
else if (xhr.readyState == 4 && xhr.status != 200) {
// Error. Inform the user
}
})
formData.append('upload_preset', 'ujpu6gyk')
formData.append('file', file)
xhr.send(formData)
}
body {
font-family: sans-serif;
}
a {
color: #369;
}
.note {
width: 500px;
margin: 50px auto;
font-size: 1.1em;
color: #333;
text-align: justify;
}
#drop-area {
border: 2px dashed #ccc;
border-radius: 20px;
width: 480px;
margin: 50px auto;
padding: 20px;
}
#drop-area.highlight {
border-color: purple;
}
p {
margin-top: 0;
}
.my-form {
margin-bottom: 10px;
}
#gallery {
margin-top: 10px;
}
#gallery img {
width: 150px;
margin-bottom: 10px;
margin-right: 10px;
vertical-align: middle;
}
.button {
display: inline-block;
padding: 10px;
background: #ccc;
cursor: pointer;
border-radius: 5px;
border: 1px solid #ccc;
}
.button:hover {
background: #ddd;
}
#fileElem {
display: none;
}
<div id="drop-area">
<form class="my-form">
<p>Upload multiple files with the file dialog or by dragging and dropping images onto the dashed region</p>
<input type="file" id="fileElem" multiple accept="image/*" onchange="handleFiles(this.files)">
<label class="button" for="fileElem">Select some files</label>
</form>
<progress id="progress-bar" max=100 value=0></progress>
<div id="gallery" /></div>
</div>

BootStrap Modal background scroll on iOS

So there is this known issue with modal on iOS, when the modal is enabled swiping up/down will scroll the body instead of the modal.
Using bootstrap 3.3.7
Tried to google it, most suggested adding
body.modal-open {
overflow: hidden !important;
}
but it doesn't work.
Some suggested,
body.modal-open {
position: fixed;
}
But background will jump to the top of the page.
So for now I am using,
body.modal-open {
overflow: hidden !important;
position: fixed;
width: 100%;
}
#exampleModal {
background: black;
}
As a work-around so the jump can't be seen(but still noticeable)
Is there other solutions to this?
This is the site i am working on http://www.einproductions.com/
I've taken the solutions of #Aditya Prasanthi and #JIm, since one fixes the background-scrolling and the other fixes the skip-to-the-top after closing the modal, and turned them into one bare-minimum JS script:
let previousScrollY = 0;
$(document).on('show.bs.modal', () => {
previousScrollY = window.scrollY;
$('html').addClass('modal-open').css({
marginTop: -previousScrollY,
overflow: 'hidden',
left: 0,
right: 0,
top: 0,
bottom: 0,
position: 'fixed',
});
}).on('hidden.bs.modal', () => {
$('html').removeClass('modal-open').css({
marginTop: 0,
overflow: 'visible',
left: 'auto',
right: 'auto',
top: 'auto',
bottom: 'auto',
position: 'static',
});
window.scrollTo(0, previousScrollY);
});
It's, of course, possible and even adviced to use a class to set and unset the CSS for the body, however, I choose this solution to resolve a problem just in one place (and not require external CSS as well).
refer to Does overflow:hidden applied to <body> work on iPhone Safari?
Added .freezePage to html and body when modal is showing
$('.modal').on('shown.bs.modal', function (e) {
$('html').addClass('freezePage');
$('body').addClass('freezePage');
});
$('.modal').on('hidden.bs.modal', function (e) {
$('html').removeClass('freezePage');
$('body').removeClass('freezePage');
});
the CSS
.freezePage{
overflow: hidden;
height: 100%;
position: relative;
}
My solution:
scrollPos = window.scrollY - get current scroll position.
body { position: fixed;
margin-top: -**scrollPos** px);
}
Then modal is closed:
body {position: "";
margin-top: "";
}
and return scroll position to point before opened modal window:
window.scrollTo(0, scrollPos);
None of the above answers worked for me, the modal kept disappearing and I ended up with a brute force approach which is ugly and inefficient but works !
$('body').on('touchstart touchmove touchend', e => {
let scrollDisabled=$('.scroll-disable');
if (scrollDisabled.length > 0 && scrollDisabled.has($(e.target)).length===0) {
e.preventDefault();
e.stopPropagation();
}
});
setInterval(() => $('.modal:visible').css('top', '20px'), 100);
$(document).on({
'show.bs.modal': e => $(e.target).addClass('scroll-disable'),
'hidden.bs.modal': e => $(e.target).removeClass('scroll-disable')
}, '.modal');
Import this file and use the enableBodyScroll and disableBodyScroll functions to lock and unlock the body scroll.
using css top property will exactly navigate back to the previous position. It eliminate the drawback of dealing with the floating point margin.
const toggleBodyScroll = (position, initialMargin) => {
document.body.style.position = position;
document.body.style.top = initialMargin;
};
const getScrolledPosition = () => {
return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
};
const scrollToPrevPosition = (scrolledPosition) => {
window.scrollTo(0, scrolledPosition);
};
const getWindowTop = () => {
return window.getComputedStyle(document.body).getPropertyValue('top');
};
export const disableBodyScroll = () => {
toggleBodyScroll('fixed', `-${getScrolledPosition()}px`);
};
export const enableBodyScroll = () => {
const scrollPosition = 0 - parseInt(getWindowTop());
toggleBodyScroll('static', 0);
scrollToPrevPosition(scrollPosition);
};
This will prevent page scrolling while Modal is opened on iOS mobile
if ($(window).width() < 960) {
let previousScrollY = 0;
$(document).on('show.bs.modal', () => {
previousScrollY = window.scrollY;
$('html').addClass('modal-open').css({
marginTop: -previousScrollY,
overflow: 'hidden',
left: 0,
right: 0,
top: 0,
bottom: 0,
position: 'fixed',
});
}).on('hidden.bs.modal', () => {
$('html').removeClass('modal-open').css({
marginTop: 0,
overflow: 'visible',
left: 'auto',
right: 'auto',
top: 'auto',
bottom: 'auto',
position: 'static',
});
window.scrollTo(0, previousScrollY);
});
}

Building a chat app that uses a node.js server in IOS [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am trying to build an iPhone(native) chat app that uses node.js on socket.io.
What is the best way to create chat application on IOS
Is there is any way to create chat application with the node.js server in IOS
Could anyone give me suggestion?
Thanks for you suggestion
Of cuz you could create chat application using Socket.io with iOS/Android and HTML!
There are 2 ways for you to do it!
i) Implement your own socket communication with Socket.io, (this is diffucult, because you need to write most of the network implementation by yourself!)
Socket.io will interface as stream, that you need to connect from your iOS!
You could refer the iOS dev guide on how to implement the stream!
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/UsingSocketsandSocketStreams.html
There is an active thread that discuss about this too!
iPhone Objective-C socket communication with Socket.IO
ii) Use existing library or wrapper that people make for iOS, you could find the link below!
This library mainly done the networking part, and you just need to implement your app logics!
iOS
https://github.com/pkyeck/socket.IO-objc
Android
https://github.com/nkzawa/socket.io-client.java
It would be good for you to start with the library first and then try to make your own implementation! :))
I suggest following:
Develop HTML5 application with node.js and NOSQL database (CouchDb in my example) on the server
Do not use socket.io for your first playground, because its complex. You must first understand the websockets very well.
Do not burden yourself with pre-prepared frameworks and tons of code. You need a clear, simple code in order to make modifications you need later. You need full control and understanding of the code you have.
I attached working sample (thanks to Ales Smodis). For the sample bellow to work, you need to install 2 node.js packets:
npm install websocket
npm install nano
And you need to create databse and insert at least 1 user into CouchDb database:
curl -X PUT http://localhost:5984/chatroom
curl -X PUT -H 'Content-Type: application/json' --data '{"username":"john","password":"john"}' http://localhost:5984/chatroom/john
$(document).ready(function () {
var connection,
username = 'Tester',
password = 'Tester',
historyCounter = 0,
members = {},
// displays
jqLogin = $('#login'),
jqChatroom = $('#chatroom'),
// login display components
jqWaiting = $('#waiting'),
jqNickname = $('#nickname'),
jqPassword = $('#password'),
// chat display components
jqHistory = $('#history'),
jqMembers = $('#members'),
jqLine = $('#line');
function addLine(nick, line) {
var jq = $('<p><span class="nick"></span><span class="line"></span></p>'),
jqNick = jq.find('.nick'),
jqLine = jq.find('.line'),
i, lines;
jqNick.text(nick ? nick + ': ' : '*** ');
jqLine.text(line);
jqHistory.append(jq);
historyCounter++;
for (lines = jqHistory.children(), i = 0; historyCounter > 100; i++, historyCounter--) {
$(lines[i]).remove();
}
}
function addMsg(msgObj) {
var msgHandler = states[activeState].messageHandlers[msgObj.type] || function () { addLine(null, 'Neveljaven paket tipa ' + msgObj.type); };
msgHandler(msgObj);
}
function clearMembers() {
var nickname;
for (nickname in members) {
members[nickname].remove();
delete members[nickname];
}
jqMembers.empty(); // just in case
}
function addMember(nickname) {
var jq = $('<li></li>');
jq.text(nickname);
jqMembers.append(jq);
members[nickname] = jq;
}
function removeMember(nickname) {
if (nickname in members) {
members[nickname].remove();
delete members[nickname];
}
}
function connect () {
connection = new WebSocket('ws://127.0.0.1:8080');
connection.onopen = function () {
states[activeState].onopen();
};
connection.onmessage = function (message) {
try {
addMsg(JSON.parse(message.data));
}
catch (e) {
addLine(null, 'Exception while handling a server message: ' + e.toString());
}
};
connection.onclose = function () {
states[activeState].onclose();
};
}
function loginKeypress(event) {
if (13 !== event.keyCode) return;
username = jqNickname.val();
password = jqPassword.val();
if (!username) jqNickname[0].focus();
else if (!password) jqPassword[0].focus();
else {
jqWaiting.css('display', '');
jqNickname.unbind('keydown', loginKeypress);
jqPassword.unbind('keydown', loginKeypress);
connect();
}
}
function inputKeypress(event) {
var line;
if (13 === event.keyCode) {
line = jqLine.val();
if (line.length === 0) return;
jqLine.val('');
connection.send(JSON.stringify({ 'type': 'line', 'line': line }));
}
}
var states = {
'login': {
'start': function () {
jqChatroom.css('display', 'none');
jqWaiting.css('display', 'none');
jqLogin.css('display', '');
jqNickname.val('');
jqPassword.val('');
jqNickname[0].focus();
activeState = 'login';
jqNickname.bind('keydown', loginKeypress);
jqPassword.bind('keydown', loginKeypress);
},
'onopen': function () {
connection.send(JSON.stringify({ 'type': 'login', 'username': username, 'password': password }));
},
'messageHandlers': {
'state': function (msgObj) {
var i, history, users;
states.chat.start();
history = msgObj.history;
jqHistory.empty();
historyCounter = 0;
for (i = 0; i < history.length; i++) addMsg(history[i]);
users = msgObj.users;
clearMembers();
for (i = 0; i < users.length; i++) addMember(users[i]);
}
},
'unhandledMessage': function (msgObj) {
connection.close(4000, 'Unhandled message type');
},
'onclose': function () {
states.login.start();
}
},
'chat': {
'start': function () {
jqLogin.css('display', 'none');
jqWaiting.css('display', 'none');
jqChatroom.css('display', '');
jqHistory.empty();
historyCounter = 0;
activeState = 'chat';
jqLine.bind('keydown', inputKeypress);
},
'onopen': function () {
connection.close(4001, 'Connection opened while chatting');
},
'messageHandlers': {
'line': function (msgObj) {
addLine(msgObj.nick, msgObj.line);
},
'join': function (msgObj) {
addLine(null, 'Priklopil: ' + msgObj.nick);
addMember(msgObj.nick);
},
'leave': function (msgObj) {
addLine(null, 'Odklopil: ' + msgObj.nick);
removeMember(msgObj.nick);
}
},
'unhandledMessage': function (msgObj) {
connection.close(4000, 'Unhandled message type');
},
'onclose': function () {
addLine(null, 'Connection closed');
jqLine.unbind('keydown', inputKeypress);
}
}
},
activeState = 'login';
states.login.start();
});
// node.js code
var http = require('http'),
url = require('url'),
path = require('path'),
fs = require('fs'),
nano = require('nano')('http://localhost:5984'),
chatroomDb = nano.use('chatroom'),
websocket = require('websocket'),
chatHistory = [],
activeUsers = {};
var filesDir = path.join(process.cwd(), 'web');
var mimeTypes = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'text/javascript'
};
var getContentType = function (extension) {
var mimeType = mimeTypes[extension];
return mimeType ? mimeType : 'application/octet-stream';
};
var server = http.createServer(function (request, response) {
var relativePath = url.parse(request.url).pathname,
absolutePath = path.join(filesDir, relativePath);
var handler = function (err, stats) {
if (stats) {
if (stats.isDirectory()) {
absolutePath = path.join(absolutePath, 'index.html');
fs.stat(absolutePath, handler);
return;
}
if (stats.isFile()) {
response.writeHead(200, getContentType(path.extname(absolutePath)));
var stream = fs.createReadStream(absolutePath);
stream.pipe(response);
return;
}
}
response.writeHead(404, {'Content-Type': 'text/plain'});
response.write('Not found\r\n');
response.end();
};
console.log('HTTP request for ' + relativePath);
fs.stat(absolutePath, handler);
});
server.listen(8080, function () {});
wsServer = new websocket.server({
'httpServer': server
});
function addLine (type, nick, line) {
var msg = { 'type': type, 'nick': nick, 'line': line },
jsonMsg = JSON.stringify(msg),
username;
chatHistory.push(msg);
while (chatHistory.length > 100) chatHistory.shift();
for (username in activeUsers) {
activeUsers[username].sendMessage(jsonMsg);
}
}
wsServer.on('request', function (request) {
console.log('New websocket connection from ' + request.origin);
// TODO: verify that request.origin is our web site
var connection = request.accept(null, request.origin);
var username = null;
connection.on('message', function (message) {
if (message.type !== 'utf8') {
console.log('Refusing a non-utf8 message');
return;
}
console.log('Processing message: ' + message.utf8Data);
try {
var m = JSON.parse(message.utf8Data);
switch (m.type) {
case 'login':
chatroomDb.get(m.username, function (err, body) {
if (err || (body.password !== m.password)) {
connection.close();
return;
}
username = m.username;
addLine('join', username, null);
activeUsers[username] = {
'sendMessage': function (jsonMsg) {
connection.sendUTF(jsonMsg);
}
};
var users = [], u;
for (u in activeUsers) users.push(u);
connection.sendUTF(JSON.stringify({ 'type': 'state', 'history': chatHistory, 'users': users }));
});
break;
case 'line':
if (!username) {
connection.close();
break;
}
addLine('line', username, m.line);
break;
}
}
catch (e) {
console.log(e);
}
});
connection.on('close', function (connection) {
console.log('Connection closed');
if (username) {
delete activeUsers[username];
addLine('leave', username, null);
}
});
});
console.log('Server running');
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Chatroom</title>
<style>
html, body {
width: 100%;
height: 100%;
padding: 0;
border: none;
margin: 0;
}
#heading {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 30px;
margin: 0;
padding: 0;
line-height: 30px;
text-align: center;
font-size: 20px;
background-color: green;
}
#outer {
position: absolute;
top: 30px;
bottom: 0;
left: 0;
right: 0;
margin: 20px;
min-height: 400px;
min-width: 400px;
background-color: lime;
}
#inner {
height: 100%;
background-color: #ffc0cb;
}
#chat {
position: absolute;
top: 0;
left: 0;
right: 200px;
bottom: 0;
background-color: #ffd700;
}
#members {
position: absolute;
top: 0;
right: 10px;
width: 180px;
bottom: 0;
background-color: #ff00ff;
list-style-type: none;
padding: 10px;
padding: 0;
border: none;
}
#history {
position: absolute;
top: 0;
left: 0;
bottom: 2em;
right: 0;
background-color: #00ffff;
padding: 10px;
}
#input {
position: absolute;
height: 2em;
left: 0;
right: 0;
bottom: 0;
background-color: #90ee90;
line-height: 2em;
}
#line {
width: 100%;
margin: 0;
border: none;
padding: 0;
}
#history > p {
margin: 2px;
}
.nick {
white-space: pre;
display: table-cell;
}
.line {
display: table-cell;
}
#login {
height: 100%;
display: table;
margin: 0 auto;
}
#login > .svg {
vertical-align: middle;
display: table-cell;
}
#login-wrapper1 {
display: table;
height: 100%;
width: 100%;
}
#login-wrapper2 {
display: table-cell;
vertical-align: middle;
}
#login-table {
margin: 0 auto;
}
#login-table .label {
text-align: right;
}
#waiting {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
opacity: 0.3;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="client.js"></script>
</head>
<body>
<div id="login">
<div class="svg">
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 400 400"
width="400"
height="400">
<defs>
<path id="curved-text" d="M0,0 c 50 -50 150 -50 200 0" />
</defs>
<g>
<text
transform="translate(-100,40)"
font-weight="bold"
font-variant="small-caps"
font-family="Arial sans-serif"
font-size="30"
fill="none"
stroke="orange"
text-anchor="middle">
<textPath xlink:href="#curved-text" startOffset="50%">Chatroom</textPath>
</text>
<animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0" to="360" dur="5s" fill="remove" additive="sum" repeatCount="indefinite" />
<animateMotion dur="10s" repeatCount="indefinite" path="M100,200 a100,100 0 1 1 200,0 a100,100 0 1 1 -200,0" />
</g>
<foreignObject
x="0"
y="0"
width="400"
height="400"
style="height:400px;">
<div xmlns="http://www.w3.org/1999/xhtml" id="login-wrapper1">
<div id="login-wrapper2">
<table id="login-table">
<tbody>
<tr>
<td class="label">Vzdevek:</td><td><input type="text" id="nickname" /></td>
</tr>
<tr>
<td class="label">Geslo:</td><td><input type="password" id="password" /></td>
</tr>
</tbody>
</table>
</div>
</div>
</foreignObject>
</svg>
</div>
</div>
<div id="chatroom" style="display:none;">
<h1 id="heading">Chatroom</h1>
<div id="outer">
<div id="inner">
<div id="chat">
<div id="history">
<p><span class="nick">Matej: </span><span class="line">Hi</span></p>
<p><span class="nick">Borut: </span><span class="line">How are you?</span></p>
<p><span class="nick">Martina: </span><span class="line">Ok, thanks!</span></p>
</div>
<div id="input"><input id="line" type="text"></div>
</div>
<ul id="members">
<li>Matej</li>
<li>Borut</li>
<li>Martina</li>
</ul>
</div>
</div>
</div>
<div id="waiting" style="display:none;"></div>
</body>
</html>
If you are evaluating options, check out IP Messaging from Twilio:
https://www.twilio.com/docs/tutorials/walkthrough/ip-chat/ios/swift#0
The tutorial above in Swift for iOS (also available for Objective-C) allows you to work with a native SDK while the server side app (yours in Node.js) generates user access tokens to connect to the API.
The code walks you through joining a channel, creating a channel and sending messages.
Note: I do work for Twilio. This quick sample app should at least help you determine what functionality will work best for you.

jqGrid:Font Awesome Icons

I am trying to use Font Awesome icons in place of the jqueryUI icons for the toolbar in my jqGrid (add,edit,delete,view icons).
This demo is exactly what I would like to accomplish. I've read Oleg's answer that demonstrates removing the icon class and adding the Font Awesome icons in its place. But when I try to do that nothing changes. I believe I'm possibly referencing the icons wrong.
I downloaded Font Awesome 4.0.3 and I have jqGrid 4.5.4--In the _icons.scss file of the FA file tree the icons are referenced like this:
.#{$fa-css-prefix}-pencil:before { content: $fa-var-pencil; }
But in Oleg's suggested code the new icons are labeled "icon-pencil":
$grid.jqGrid("navGrid", "#pager", {editicon: "icon-pencil",
addicon: "icon-plus", delicon: "icon-trash", searchicon: "icon-search",
refreshicon: "icon-refresh", viewicon: "icon-file",view: true});
$("#pager .navtable .ui-pg-div>span").removeClass("ui-icon");
This is my code: I only did the edit icon for this example. I also used the new label for the icons, "fa-pencil".
jQuery("#grid").jqGrid('navGrid','#grid_toppager"', {editicon: "fa-pencil", edit:true});
$('#grid_toppager .navtable .ui-pg-div>span').removeClass('ui-icon');
What combination of code do I need in order to replace the ui-icons with the Font Awesome icons?
Any helpful tips would be appreciated, thanks
I agree that my old answer can't be used with Font Awesome 4 because the names of the classes are changed in version 4. I use Font Awesome 4 myself in solutions which I develop for my customers and I decide to share it with other.
The files jQuery.jqGrid.fontAwesome4.css, jQuery.jqGrid.fontAwesome4.js and jQuery.jqGrid.checkboxFontAwesome4.js contains new jqGrid method initFontAwesome and formatter: "checkboxFontAwesome4". The demo demonstrates the usage of the files:
The usage of suggested method initFontAwesome is very simple. First of all one need to include additional CSS and JavaScript files:
<link rel="stylesheet" type="text/css"
href="http://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css">
...
<link rel="stylesheet" type="text/css" href=".../ui.jqgrid.css" />
<link rel="stylesheet" type="text/css" href=".../jQuery.jqGrid.fontAwesome4.css" />
...
<script type="text/javascript" src=".../i18n/grid.locale-en.js"></script>
<script type="text/javascript" src=".../jquery.jqGrid.min.js"></script>
<script type="text/javascript" src=".../jQuery.jqGrid.fontAwesome4.js"></script>
Then one modify well known line
$("#grid").jqGrid({
... // jqGrid options and callbacks
});
to
$("#grid").jqGrid("initFontAwesome").jqGrid({
... // jqGrid options and callbacks
});
To use formatter: "checkboxFontAwesome4" instead of predefined formatter formatter: "checkbox" one need just includes jQuery.jqGrid.checkboxFontAwesome4.js after jquery.jqGrid.min.js (or jquery.jqGrid.src.js):
<script type="text/javascript"
src=".../jQuery.jqGrid.checkboxFontAwesome4.js"></script>
The formatter "checkboxFontAwesome4" have some advantage to formatter: "checkbox":
one can select the row by clicking on the icons. The standard formatter: "checkbox" uses disabled <input type="checkbox">. Clicking on disabled control will be blocked on the most web browsers. I posted before "clickableCheckbox" (see here and here).
The tests which I made with grids having many rows and columns using the tree checkbox formatters shows that formatter "checkboxFontAwesome4" is the most quick in rendering (in all web browsers where I it tested), formatter: "checkbox" is lower and "clickableCheckbox" is the mostly slow. So formatter "checkboxFontAwesome4" is not only cool, but it's really quick in rendering.
At the end I includes the current state of jQuery.jqGrid.fontAwesome4.css, jQuery.jqGrid.fontAwesome4.js and jQuery.jqGrid.checkboxFontAwesome4.js:
jQuery.jqGrid.fontAwesome4.css:
.ui-jqgrid .ui-pg-table .ui-pg-div>span.fa, #jqContextMenu .ui-menu-item>a>span.fa {
text-indent:0;
height: auto;
width: auto;
background-image: none;
overflow: visible;
padding-top: 1px;
}
.ui-jqgrid .ui-pg-table .ui-pg-div {
text-indent:0;
height: auto;
width: auto;
background-image: none;
overflow: visible;
padding-top: 1px;
}
.ui-jqgrid .ui-jqgrid-titlebar-close.fa-title span { font-size: 18px; display: inline-block; }
.ui-jqgrid .ui-jqgrid-titlebar-close.fa-title { margin-top: 0; top: 0; padding-left: 2px; padding-bottom: 2px;}
.ui-jqgrid .ui-icon-asc.fa { height: auto; margin-top: 0; }
.ui-jqgrid .ui-icon-asc.fa, .ui-jqgrid .ui-icon-desc.fa {
height: auto; margin-top: 2px; margin-left: 2px;
}
.ui-jqgrid .s-ico>.ui-state-disabled.fa, .s-ico>.ui-state-disabled.fa { padding: 0; }
.ui-jqdialog .ui-jqdialog-titlebar-close { text-decoration: none; right: 0.2em !important}
.ui-jqdialog .ui-jqdialog-titlebar-close>span { margin-top: 3px; margin-left: 5px;}
.ui-jqdialog .EditTable .fm-button-icon-right { padding-left: 0; padding-right: 0.5em; float:right;}
.ui-jqdialog .EditTable .fm-button-icon-left { padding-left: 0; float:left; }
.ui-jqdialog .EditButton>.fm-button { display: block; width: auto; }
.ui-jqdialog .EditButton>.fm-button>span { float: left; margin-left: 0.5em; margin-right: 0;}
.ui-jqgrid .ui-jqdialog .fm-button>span { margin-left: 0.5em; margin-right: 0; }
.ui-jqdialog>.ui-resizable-se { bottom: -3px; right: -3px}
jQuery.jqGrid.fontAwesome4.js:
/*global $ */
(function ($) {
"use strict";
/*jslint unparam: true */
$.extend($.jgrid, {
icons: {
common: "fa", // will be implemented later
scale: "", // will be implemented later. For example as "fa-lg"
titleVisibleGrid: "fa fa-arrow-circle-up",
titleHiddenGrid: "fa fa-arrow-circle-down",
titleIcon: "ui-corner-all fa-title",
close: "fa fa-times",
edit: "fa fa-pencil fa-fw",
add: "fa fa-plus fa-fw",
del: "fa fa-trash-o fa-fw",
search: "fa fa-search fa-fw",
refresh: "fa fa-refresh fa-fw",
view: "fa fa-file-o fa-fw",
pager: {
first: "fa fa-step-backward fa-fw",
prev: "fa fa-backward fa-fw",
next: "fa fa-forward fa-fw",
last: "fa fa-step-forward fa-fw"
},
form: {
prev: "fa fa-caret-left",
next: "fa fa-caret-right",
save: "fa fa-floppy-o",
undo: "fa fa-undo",
close: "fa fa-times",
delete: "fa fa-trash-o"
},
searchForm: {
reset: "fa fa-undo",
query: "fa fa-comments-o",
search: "fa fa-search"
}
}
});
$.extend($.jgrid.nav, {
editicon: $.jgrid.icons.edit,
addicon: $.jgrid.icons.add,
delicon: $.jgrid.icons.del,
searchicon: $.jgrid.icons.search,
refreshicon: $.jgrid.icons.refresh,
viewicon: $.jgrid.icons.view
});
$.extend($.jgrid.defaults, {
fontAwesomeIcons: true // the new option will be used in callbacks
});
$.extend($.jgrid, {
originalCreateModal: $.jgrid.originalCreateModal || $.jgrid.createModal,
createModal: function (aIDs, content, p, insertSelector, posSelector, appendsel, css) {
$.jgrid.originalCreateModal.call(this, aIDs, content, p, insertSelector, posSelector, appendsel, css);
if ($(insertSelector).find(">.ui-jqgrid-bdiv>div>.ui-jqgrid-btable").jqGrid("getGridParam", "fontAwesomeIcons")) {
$("#" + $.jgrid.jqID(aIDs.modalhead) + ">a.ui-jqdialog-titlebar-close>span.ui-icon")
.removeClass("ui-icon ui-icon-closethick")
.addClass($.jgrid.icons.close);
$("#" + $.jgrid.jqID(aIDs.themodal) + ">div.jqResize").removeClass("ui-icon-grip-diagonal-se");
}
}
});
$.extend($.jgrid.view, {
beforeShowForm: function ($form) {
var $dialog = $form.closest(".ui-jqdialog"),
$iconSpans = $dialog.find("a.fm-button>span.ui-icon");
$iconSpans.each(function () {
var $this = $(this), $fmButton = $this.parent();
if ($this.hasClass("ui-icon-triangle-1-w")) {
$this.removeClass("ui-icon ui-icon-triangle-1-w")
.addClass($.jgrid.icons.form.prev);
} else if ($this.hasClass("ui-icon-triangle-1-e")) {
$this.removeClass("ui-icon ui-icon-triangle-1-e")
.addClass($.jgrid.icons.form.next);
} else if ($this.hasClass("ui-icon-close")) {
$fmButton.removeClass("fm-button-icon-left")
.addClass("fm-button-icon-right")
.html("<span class=\"" + $.jgrid.icons.form.close + "\"></span><span>" + $fmButton.text() + "</span>");
}
});
}
});
$.extend($.jgrid.del, {
afterShowForm: function ($form) {
var $dialog = $form.closest(".ui-jqdialog"),
$tdButtons = $dialog.find(".EditTable .DelButton"),
$fmButtonNew = $("<td class=\"DelButton EditButton\" style=\"float: right;\">"),
$iconSpans = $tdButtons.find(">a.fm-button>span.ui-icon");
$tdButtons.css("float", "right");
$iconSpans.each(function () {
var $this = $(this), $fmButton = $this.parent();
if ($this.hasClass("ui-icon-scissors")) {
$fmButton.html("<span class=\"" + $.jgrid.icons.form.delete + "\"></span><span>" + $fmButton.text() + "</span>");
$fmButtonNew.append($fmButton);
} else if ($this.hasClass("ui-icon-cancel")) {
$fmButton.html("<span class=\"" + $.jgrid.icons.form.undo + "\"></span><span>" + $fmButton.text() + "</span>");
$fmButtonNew.append($fmButton);
}
});
if ($fmButtonNew.children().length > 0) {
// remove between buttons
$tdButtons.replaceWith($fmButtonNew);
}
}
});
$.jgrid.extend({
initFontAwesome: function () {
return this.each(function () {
var $grid = $(this);
$grid.bind("jqGridFilterAfterShow", function (e, $form) {
// an alternative to afterShowSearch
var $dialog = $form.closest(".ui-jqdialog"),
$iconSpans = $dialog.find("a.fm-button>span.ui-icon");
$iconSpans.each(function () {
var $this = $(this), $fmButton = $this.parent();
$this.removeClass("ui-icon");
if ($this.hasClass("ui-icon-search")) {
$this.closest(".EditButton").css("float", "right");
$fmButton.addClass("fm-button-icon-right")
.html("<span class=\"" + $.jgrid.icons.searchForm.search + "\"></span><span>" + $fmButton.text() + "</span>");
} else if ($this.hasClass("ui-icon-arrowreturnthick-1-w")) {
$this.closest(".EditButton").css("float", "left");
$fmButton.addClass("fm-button-icon-left")
.html("<span class=\"" + $.jgrid.icons.searchForm.reset + "\"></span><span>" + $fmButton.text() + "</span>");
} else if ($this.hasClass("ui-icon-comment")) {
$this.closest(".EditButton").css("float", "right");
$fmButton.addClass("fm-button-icon-right")
.html("<span class=\"" + $.jgrid.icons.searchForm.query + "\"></span><span>" + $fmButton.text() + "</span>");
}
});
}).bind("jqGridAddEditBeforeShowForm", function (e, $form) {
// alternative to beforeShowForm callback
var $dialog = $form.closest(".ui-jqdialog"),
$iconSpans = $dialog.find("a.fm-button>span.ui-icon");
$iconSpans.each(function () {
var $this = $(this), $fmButton = $this.parent();
if ($this.hasClass("ui-icon-triangle-1-w")) {
$this.removeClass("ui-icon ui-icon-triangle-1-w")
.addClass($.jgrid.icons.form.prev);
} else if ($this.hasClass("ui-icon-triangle-1-e")) {
$this.removeClass("ui-icon ui-icon-triangle-1-e")
.addClass($.jgrid.icons.form.next);
} else if ($this.hasClass("ui-icon-disk")) {
$this.closest(".EditButton").css("float", "right");
$fmButton.html("<span class=\"" + $.jgrid.icons.form.save + "\"></span><span>" + $fmButton.text() + "</span>");
} else if ($this.hasClass("ui-icon-close")) {
$this.closest(".EditButton").css("float", "right");
$fmButton.removeClass("fm-button-icon-left")
.addClass("fm-button-icon-right")
.html("<span class=\"" + $.jgrid.icons.form.undo + "\"></span><span>" + $fmButton.text() + "</span>");
}
});
}).bind("jqGridHeaderClick", function (e, gridstate) {
var $icon;
if (this.p.fontAwesomeIcons) {
$icon = $(this).closest(".ui-jqgrid").find(".ui-jqgrid-titlebar>.ui-jqgrid-titlebar-close>span");
if (gridstate === "visible") {
$icon.removeClass("ui-icon ui-icon-circle-triangle-n fa-arrow-circle-down")
.addClass($.jgrid.icons.titleVisibleGrid).parent().addClass($.jgrid.icons.titleIcon);
} else if (gridstate === "hidden") {
$icon.removeClass("ui-icon ui-icon-circle-triangle-n fa-arrow-circle-up")
.addClass($.jgrid.icons.titleHiddenGrid).parent().addClass($.jgrid.icons.titleIcon);
}
}
}).bind("jqGridInitGrid", function () {
var $this = $(this), $pager, $sortables;
if (this.p.fontAwesomeIcons) {
$pager = $this.closest(".ui-jqgrid").find(".ui-pg-table");
$pager.find(".ui-pg-button>span.ui-icon-seek-first")
.removeClass("ui-icon ui-icon-seek-first")
.addClass($.jgrid.icons.pager.first);
$pager.find(".ui-pg-button>span.ui-icon-seek-prev")
.removeClass("ui-icon ui-icon-seek-prev")
.addClass($.jgrid.icons.pager.prev);
$pager.find(".ui-pg-button>span.ui-icon-seek-next")
.removeClass("ui-icon ui-icon-seek-next")
.addClass($.jgrid.icons.pager.next);
$pager.find(".ui-pg-button>span.ui-icon-seek-end")
.removeClass("ui-icon ui-icon-seek-end")
.addClass($.jgrid.icons.pager.last);
$this.closest(".ui-jqgrid")
.find(".ui-jqgrid-titlebar>.ui-jqgrid-titlebar-close>.ui-icon-circle-triangle-n")
.removeClass("ui-icon ui-icon-circle-triangle-n")
.addClass("fa fa-arrow-circle-up").parent().addClass("ui-corner-all fa-title");
$sortables = $this.closest(".ui-jqgrid")
.find(".ui-jqgrid-htable .ui-jqgrid-labels .ui-jqgrid-sortable span.s-ico");
$sortables.find(">span.ui-icon-triangle-1-s")
.removeClass("ui-icon ui-icon-triangle-1-s")
.addClass("fa fa-sort-asc fa-lg");
$sortables.find(">span.ui-icon-triangle-1-n")
.removeClass("ui-icon ui-icon-triangle-1-n")
.addClass("fa fa-sort-desc fa-lg");
}
});
});
}
});
}(jQuery));
jQuery.jqGrid.checkboxFontAwesome4.js:
/*global jQuery */
(function ($) {
"use strict";
/*jslint unparam: true */
$.extend($.fn.fmatter, {
checkboxFontAwesome4: function (cellValue, options) {
var title = options.colModel.title !== false ? ' title="' + (options.colName || options.colModel.label || options.colModel.name) + '"' : '';
return (cellValue === 1 || String(cellValue) === "1" || cellValue === true || String(cellValue).toLowerCase() === "true") ?
'<i class="fa fa-check-square-o fa-lg"' + title + '></i>' :
'<i class="fa fa-square-o fa-lg"' + title + '></i>';
}
});
$.extend($.fn.fmatter.checkboxFontAwesome4, {
unformat: function (cellValue, options, elem) {
var cbv = (options.colModel.editoptions) ? options.colModel.editoptions.value.split(":") : ["Yes", "No"];
return $(">i", elem).hasClass("fa-check-square-o") ? cbv[0] : cbv[1];
}
});
}(jQuery));
UPDATED: Another demo contains some additional CSS styles which improve visibility of jqGrid if one includes bootstrap.css of the Bootstrap 3.0.2. I am sure that the styles are not the best, but there fix the problems which I found in my tests. Below are the styles:
.ui-jqgrid .ui-pg-table .ui-pg-input, .ui-jqgrid .ui-pg-table .ui-pg-selbox {
height: auto;
width: auto;
line-height: inherit;
}
.ui-jqgrid .ui-pg-table .ui-pg-selbox {
padding: 1px;
}
.ui-jqgrid { line-height: normal; }
div.ui-jqgrid-view table.ui-jqgrid-btable {
border-style: none;
border-top-style: none;
border-collapse: separate;
}
.ui-jqgrid .ui-jqgrid-titlebar-close.fa-title {
border-collapse: separate;
margin-top: 0;
top: 0;
margin-right: 2px;
height: 22px;
width: 20px;
padding: 2px;
}
.ui-jqgrid .ui-jqgrid-titlebar-close.fa-title.ui-state-hover span {
margin-top: -1px;
margin-left: -1px;
}
.ui-paging-info { display: inline; }
.ui-jqgrid .ui-pg-table { border-collapse: separate; }
div.ui-jqgrid-view table.ui-jqgrid-btable td {
border-left-style: none
}
div.ui-jqgrid-view table.ui-jqgrid-htable {
border-style: none;
border-top-style: none;
border-collapse: separate;
}
div.ui-jqgrid-view table.ui-jqgrid-btable th {
border-left-style: none
}
.ui-jqgrid .ui-jqgrid-htable th div {
height: 14px;
}
.ui-jqgrid .ui-jqgrid-resize {
height: 18px !important;
}
UPDATED 2: One more demo works with Font Awesome 4.2 and Bootstrap 3.2. The usage is very easy. One should include some .css (jQuery.jqGrid.fontAwesome4.css and jQuery.jqGrid.bootstrap-fixes.css) and .js files (jQuery.jqGrid.fontAwesome4.js and jQuery.jqGrid.checkboxFontAwesome4.js) and to use .jqGrid("initFontAwesome") before the grid are created. To fix problems with height of editing form at the second opening I used beforeInitData: function () { $("#editmod" + this.id).remove(); } additionally. One can download the latest versions of jQuery.jqGrid.fontAwesome4.css, jQuery.jqGrid.bootstrap-fixes.css, jQuery.jqGrid.fontAwesome4.js and jQuery.jqGrid.checkboxFontAwesome4.js from here.
For custom buttons, here is quick and... work around:
$(grid).jqGrid('navButtonAdd', pager, {
buttonicon: 'none',
caption: '<span class="my-fa-icon fa fa-barcode"></span> My Caption Here',
id: 'btnMyButton'
})
if you need to change the caption dynamically, update the div (representing the button) html (not text):
var myButton = $($(grid)[0].p.pager + '_left ' + 'td#btnMyButton');
$(myButton ).html('<span class="my-fa-icon fa fa-barcode"></span> My NEW Caption Here');
I included a css class, .my-fa-icon, just in case you want to add some customization (and make the display closer to what jqGrid does) - for example, you can add this to your css file:
.ui-jqgrid .ui-jqgrid-pager .ui-pg-button .ui-pg-div span.my-fa-icon { margin: 0 2px; width: 1.4em; font-size: 1.4em; float: left; overflow: hidden; }

Issue with hidden div-jquery

i'm using jquery hashchange technique for dynamically and smoothly(fadein) loading the contents in a website.Though it's dynamic the url gets changed in the addressbar which looks as follows..as you can see a pound symbol appears before filename.
www.whatever.com/#about.html
www.whatever.com/#contact.html and so on.
In my index page a div named 'folder1' is hidden by default and it's made visible 5seconds after the page is loaded and there is div folder2(check the code).
When you type url 'www.whatever.com' everything works as it should. But when you hit 'home' link it appends # to index.html so url will be whatever.com/#index.html.
And this time div 'folder2' show up right after page is loaded which should be hidden as per the code. I noticed css of those divs gets messed up this time.
I don't understand what's happpening there. Any help?
(function($,i,b){var j,k=$.event.special,c="location",d="hashchange",l="href",f=$.browser,g=document.documentMode,h=f.msie&&(g===b||g<8),e="on"+d in i&&!h;function a(m){m=m||i[c][l];return m.replace(/^[^#]*#?(.*)$/,"$1")}$[d+"Delay"]=100;k[d]=$.extend(k[d],{setup:function(){if(e){return false}$(j.start)},teardown:function(){if(e){return false}$(j.stop)}});j=(function(){var m={},r,n,o,q;function p(){o=q=function(s){return s};if(h){n=$('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this);
$(function() {
var newHash = "",
$mainContent = $("#main-content"),
$pageWrap = $("#page-wrap"),
baseHeight = 0,
$el;
$pageWrap.height($pageWrap.height());
baseHeight = $pageWrap.height() - $mainContent.height();
$("nav").delegate("a", "click", function() {
window.location.hash = $(this).attr("href");
return false;
});
$(window).bind('hashchange', function(){
newHash = window.location.hash.substring(1);
if (newHash) {
$mainContent
.find("#guts")
.fadeOut(200, function() {
$mainContent.hide().load(newHash + " #guts", function() {
$mainContent.fadeIn(200, function() {
$pageWrap.animate({
height: baseHeight + $mainContent.height() + "px"
});
});
$("nav a").removeClass("current");
$("nav a[href="+newHash+"]").addClass("current");
});
});
};
});
$(window).trigger('hashchange');
});
above makes the entire jquery code.
this is the css.infact there are two divs.
#folder1{
float: left;
height: 100px;
width: 100px;
position: absolute;
top: 15px;
right: 100px;
display: none;
}
#folder2{
float: left;
height: 100px;
width: 100px;
position: absolute;
top: 15px;
right: 100px;
display: show;
}
below is the code usedto hide and show divs.
$(document).ready(function() {
$("#folder2").hide();
setTimeout(function(){
$("#folder1").show();
}, 5000);
setTimeout(function(){
$("#folder1").hide();
}, 10000);
setTimeout(function(){
$("#folder2").show();
}, 15000);
});

Resources