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

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>

Related

How can an ag-Grid cell editor in Svelte prompt the user to confirm a change?

I have an ag-Grid in a Svelte file.
One of the column definitions is for a floating point number displayed to 2 places of decimals, like this:
const columnDefinitions = [
...
{
field: fixedScr,
headerName: "Fixed SCR",
cellClass: numberCellClassSelector,
type: "rightAligned",
width: 150,
editable: true,
valueFormatter: numberFormatterFactory(2),
valueParser: numberParser,
},
...
];
I have chosen the ag-Grid as a convenient means of displaying and editing a column of these values. However, my Product Owner wants the web page to challenge the user every time they make a change to a cell with an "Are you sure?" prompt.
A bit heavy-handed, perhaps, as it will make editing with the ag-Grid somewhat slower. But these values will be change infrequently, and changes should be made with care.
How would I define a simple cell editor, just for this column, which prompts the user to confirm a change before the grid is updated?
I would propose binding into an ag-grid event which is triggered once a value is updated. on the callback (which should by an async function).
my implementation will go as follow create a Popup.svelte component.
you will also create a store, which i will call popup in a global js file for example store.js.
you will then import popup from store.js in Popup.svelte. then you will set the value of the popup store to an async function which will interact with the HTML of Popup.svelte. this async function will return a promise which you will await in your other svelte components while using the popup store.
in this Promise you will await all previous popups to close to show your current popup, you will supply the title, and the return values of the buttons which will be shown in the popup
here is an example of the implementation of the code i made
<style>
.u-overlay {
min-height: 100vh;
max-height: 100vh;
width: 100%;
position: fixed;
display: flex;
background-color: rgba(0, 0, 0, 0.5);
z-index: 50000;
}
.u-box {
width: 500px;
background-color: white;
min-height: 100px;
margin: auto;
padding: 20px;
border-radius: 4px;
}
.u-title {
width: 100%;
text-align: center;
font-size: 22px;
}
.u-desc {
padding: 20px 20px;
text-align: center;
margin: 0;
}
.u-buttons {
width: 100%;
display: flex;
padding: 10px 0;
justify-content: space-evenly;
}
.u-over-button {
width: 150px;
border: 1px solid transparent;
border-radius: 4px;
color: white;
text-align: center;
padding: 8px 0;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
cursor: pointer;
}
</style>
<script>
import { popup } from "../utils.js";
import { fade } from "svelte/transition";
let overlay;
let template = {
title: "Write title",
desc: "Please select",
buttons: [
{ name: "OK", value: true, color: "#F0F0F0" },
{ name: "Decline", value: false, color: "red" },
],
};
let popData = undefined;
let promises = [];
let colorsConver = {
ok: "#46b978",
danger: "#d23149",
};
popup.set(async (data) => {
/* we got a new sub*/
/* start the promise for the future click */
let pro = new Promise(async (resolve, reject) => {
/* make sure all promises before this one are done */
await Promise.all(promises);
/* when they are done start the overlay for this sub */
/* convert text to appropriate hex */
for (let btn of data.buttons) {
if (colorsConver[btn.color]) {
btn.color = colorsConver[btn.color];
}
}
popData = data;
setTimeout(() => {
overlay.addEventListener(
"click",
(event) => {
if (event.target !== event.currentTarget) return;
event.stopPropagation();
console.log("from overlay");
resolve(data.buttons[data.buttons.length - 1].value);
popData = undefined;
},
{
once: true,
capture: true,
}
);
for (let b of [
...document.querySelectorAll(".u-overlay .u-buttons"),
]) {
b.addEventListener(
"click",
(event) => {
event.stopPropagation();
console.log("ending button");
resolve(event.target.dataset.res);
popData = undefined;
},
{
once: true,
capture: true,
}
);
}
}, 130);
});
/* add this promise so the future ones wait it*/
promises.push(pro);
return pro;
});
import { popup } from "path/to/store.js";
const someFunction = () => {
let resp = await $popup({
title: "Write title",
desc: "Please select",
buttons: [
{ name: "OK", value: true, color: "#F0F0F0" },
{ name: "Decline", value: false, color: "red" },
],
});
};
</script>
{#if popData}
<div
bind:this={overlay}
transition:fade={{ duration: 150 }}
class="u-overlay"
>
<div on:click|stopPropagation|preventDefault class="u-box">
<div class="u-title">{popData.title}</div>
<p class="u-desc">{popData.desc}</p>
<div class="u-buttons">
{#each popData.buttons as b}
<div
data-res={b.value}
class="u-over-button"
style={"background-color:" + b.color}
>
{b.name}
</div>
{/each}
</div>
</div>
</div>
{/if}
in other components
<script>
import { popup } from "path/to/store.js";
const someFunction = () => {
let resp = await $popup({
title: "Write title",
desc: "Please select",
buttons: [
{ name: "OK", value: true, color: "#F0F0F0" },
{ name: "Decline", value: false, color: "red" },
],
});
};
</script>

Angular material progress spinner

Does anyone know how to show the incomplete portion in material progress spinner when the mode is determinate.Now I'm getting like this
.
but I want like this
This can be done but it's mostly a hack. The idea is to use a div with a border that matches the spinner and place it behind the spinner.
Example on StackBlitz
<div class="spinner-container">
<div class="spinner-background">{{spinner.value}}%</div>
<mat-progress-spinner #spinner
color="primary"
mode="determinate"
value="75">
</mat-progress-spinner>
</div>
The trick is the div styling, which needs to be sized and positioned to match your spinner exactly:
.spinner-container {
position: relative;
}
.spinner-background {
position: absolute;
width: 80px;
height: 80px;
line-height: 80px;
text-align: center;
overflow: hidden;
border-color: rgba(103, 58, 183, 0.12);
border-radius: 50%;
border-style: solid;
border-width: 10px;
}
EDIT:
I built a simple wrapper component for this that handles sizing and theme coloring automatically:
StackBlitz
spinner-container.ts:
import { coerceNumberProperty } from '#angular/cdk/coercion';
import { AfterViewInit, Component, ElementRef, Input, SimpleChanges } from '#angular/core';
import { CanColor, mixinColor, ThemePalette } from '#angular/material/core';
const BASE_SIZE = 100;
const BASE_STROKE_WIDTH = 10;
export class SpinnerContainerBase {
constructor(public _elementRef: ElementRef) { }
}
export const _SpinnerContainerMixinBase = mixinColor(SpinnerContainerBase, 'primary');
/**
* #title Progress spinner container for spinner circle background and value display
*/
#Component({
selector: 'spinner-container',
templateUrl: 'spinner-container.html',
styleUrls: ['spinner-container.scss'],
host: {
'class': 'spinner-container',
'[style.width.px]': 'diameter',
'[style.height.px]': 'diameter',
'[style.line-height.px]': 'diameter'
}
})
export class SpinnerContainer extends _SpinnerContainerMixinBase implements AfterViewInit, CanColor {
constructor(public _elementRef: ElementRef) {
super(_elementRef);
}
#Input() color: ThemePalette = 'primary';
#Input()
get diameter(): number { return this._diameter; }
set diameter(size: number) {
this._diameter = coerceNumberProperty(size);
}
private _diameter: number = BASE_SIZE;
#Input() displayWith: (number) => string | number;
#Input()
get strokeWidth() { return this._strokeWidth; }
set strokeWidth(newValue: number) {
if (newValue) {
this._strokeWidth = Math.min(this.diameter / 2, coerceNumberProperty(newValue));
if (this._spinnerBackgroundElement) {
this._spinnerBackgroundElement.style.borderWidth = this.strokeWidth + 'px';
}
}
}
private _strokeWidth: number = BASE_STROKE_WIDTH;
#Input()
get value(): number { return this._value; }
set value(newValue: number) {
this._value = Math.max(0, Math.min(100, coerceNumberProperty(newValue)));
}
private _value: number = 0;
private _spinnerBackgroundElement: HTMLElement;
ngAfterViewInit() {
this._spinnerBackgroundElement = this._elementRef.nativeElement.querySelector('.spinner-background');
this._spinnerBackgroundElement.style.borderWidth = this.strokeWidth + 'px';
}
}
spinner-container.html
<div class="spinner-value" *ngIf="displayWith">{{displayWith(value)}}</div>
<div class="spinner-background"></div>
<mat-progress-spinner
[color]="color"
[diameter]="diameter"
mode="determinate"
[strokeWidth]="strokeWidth"
[value]="value">
</mat-progress-spinner>
spinner-container.scss
:host {
display: block;
position: relative;
.spinner-value, .spinner-background {
position: absolute;
width: inherit;
height: inherit;
}
.spinner-value {
text-align: center;
overflow: hidden;
}
.spinner-background {
opacity: .12;
box-sizing: border-box;
border-radius: 50%;
border-style: solid;
}
}
_spinner-container-theme.scss
#mixin spinner-container-theme($theme) {
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$warn: map-get($theme, warn);
.spinner-background {
.spinner-container.mat-primary & {
color: mat-color($primary);
}
.spinner-container.mat-accent & {
color: mat-color($accent);
}
.spinner-container.mat-warn & {
color: mat-color($warn);
}
}
}

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; }

Printing page with bootstrap 3

I've been looking through a lot of answers here on stackoverflow that semi-cover what I'm wondering about, but haven't found anything that worked for me.
I understand that the printing page is about 550 px for A4 and therefor bootstrap will use the styles and layout usually used for mobile devices.
When I use Ctrl+P for my web page, the printable page looks just like the mobile version of my page. But how do I make it look like the desktop version? (media > 1024 px) Is there a way to do this?
I know I can change the css specifically for print. But how to solve this thing with the bootstrap 3 grid system? The width on my divs gets based on what I have added for col-xs, but I want print to use the layout (width) for col-md
Edit: After I have been struggeling with this for some more hours I realize that it might be more complex than I first expected. Just changing the width doesn't solve it for me. Many of my divs has the syntax of
<div class="md-right sm-right xs-down col-md-1 col-sm-2 box"></div>
or
<div class="col-md-4 col-sm-6 col-xs-12"></div>
The page looks good in XS for small devices, but printing in XS makes many elements look gigantic. So the question remains. Is there a way to make the printing page look the same as the layout for medium or large devices? Or do I have to make the printing css without using bootstrap grid system and add static widths in pt for all elements to accomplish this?
Thanks in advance
It would be helpful to provide a JSBin. Anyway, since I had this layout in JSBin with col-sm-(asterisk), you can just change all the -sm- to -xs- in between the print media query. All percentages are the same at every breakpoint, so changing sm to xs will print that and ignore the other col-(asterisk) classes. Ahh, I read the post now, you'll need to change all the col-sm to col-md in this and then use !important, that should do it. The xs col are outside media queries, so that's why this is happening.
http://jsbin.com/AzICaQes/5
#media print {
.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
float: left;
}
.col-sm-12 {
width: 100%;
}
.col-sm-11 {
width: 91.66666666666666%;
}
.col-sm-10 {
width: 83.33333333333334%;
}
.col-sm-9 {
width: 75%;
}
.col-sm-8 {
width: 66.66666666666666%;
}
.col-sm-7 {
width: 58.333333333333336%;
}
.col-sm-6 {
width: 50%;
}
.col-sm-5 {
width: 41.66666666666667%;
}
.col-sm-4 {
width: 33.33333333333333%;
}
.col-sm-3 {
width: 25%;
}
.col-sm-2 {
width: 16.666666666666664%;
}
.col-sm-1 {
width: 8.333333333333332%;
}
}
Actually, all you need to add (to user.css) is this:
#media print {
#page {
size: A4;
margin: 0mm;
}
html, body {
width: 1024px;
}
body {
margin: 0 auto;
}
}
While you're at it, consider using all these settings for bootstrap 3
#media print {
#page {
size: A4;
margin: 0mm;
}
html, body {
width: 1024px;
}
body {
margin: 0 auto;
line-height: 1em;
word-spacing:1px;
letter-spacing:0.2px;
font: 14px "Times New Roman", Times, serif;
background:white;
color:black;
width: 100%;
float: none;
}
/* avoid page-breaks inside a listingContainer*/
.listingContainer{
page-break-inside: avoid;
}
h1 {
font: 28px "Times New Roman", Times, serif;
}
h2 {
font: 24px "Times New Roman", Times, serif;
}
h3 {
font: 20px "Times New Roman", Times, serif;
}
/* Improve colour contrast of links */
a:link, a:visited {
color: #781351
}
/* URL */
a:link, a:visited {
background: transparent;
color:#333;
text-decoration:none;
}
a[href]:after {
content: "" !important;
}
a[href^="http://"] {
color:#000;
}
#header {
height:75px;
font-size: 24pt;
color:black
}
}
I ended up solving the problem using an own print.css file without all the responsive bootstrap stuff included. Only included some of the necessary bootstrap things with show and hide.
For those who use bootstrap mixins to create columns like this (sass version):
#include make-sm-column(3, 0);
it won't be enough to overwrite the styles for columns classes like Christina suggests. The only simple solution I've found was to change $screen-sm in _variables.scss to 595px and recompile bootstrap.css
So, find this code in _variables.scss:
$screen-sm: 768px !default;
$screen-sm-min: $screen-sm !default;
and change it to this:
// decrease min-width to fix print layout issue
$screen-sm: 595px !default;
$screen-sm-min: $screen-sm !default;
then in your _print.scss
#page {
size: A4;
margin: 0;
}
#media print {
html, body {
width: 768px;
}
body {
margin: 0 auto;
}
// .. your custom styles for print layout
}
Add This css style in your print.css file
#page {
size: A4;
margin: 40px;
}
#media print {
html,
body {
width: 210mm;
height: 297mm;
}
#-moz-document url-prefix() {}
.col-sm-1,
.col-sm-2,
.col-sm-3,
.col-sm-4,
.col-sm-5,
.col-sm-6,
.col-sm-7,
.col-sm-8,
.col-sm-9,
.col-sm-10,
.col-sm-11,
.col-sm-12,
.col-md-1,
.col-md-2,
.col-md-3,
.col-md-4,
.col-md-5,
.col-md-6,
.col-md-7,
.col-md-8,
.col-md-9,
.col-md-10,
.col-md-11,
.col-smdm-12 {
float: left;
}
.col-sm-12,
.col-md-12 {
width: 100%;
}
.col-sm-11,
.col-md-11 {
width: 91.66666667%;
}
.col-sm-10,
.col-md-10 {
width: 83.33333333%;
}
.col-sm-9,
.col-md-9 {
width: 75%;
}
.col-sm-8,
.col-md-8 {
width: 66.66666667%;
}
.col-sm-7,
.col-md-7 {
width: 58.33333333%;
}
.col-sm-6,
.col-md-6 {
width: 50%;
}
.col-sm-5,
.col-md-5 {
width: 41.66666667%;
}
.col-sm-4,
.col-md-4 {
width: 33.33333333%;
}
.col-sm-3,
.col-md-3 {
width: 25%;
}
.col-sm-2,
.col-md-2 {
width: 16.66666667%;
}
.col-sm-1,
.col-md-1 {
width: 8.33333333%;
}
.col-sm-pull-12 {
right: 100%;
}
.col-sm-pull-11 {
right: 91.66666667%;
}
.col-sm-pull-10 {
right: 83.33333333%;
}
.col-sm-pull-9 {
right: 75%;
}
.col-sm-pull-8 {
right: 66.66666667%;
}
.col-sm-pull-7 {
right: 58.33333333%;
}
.col-sm-pull-6 {
right: 50%;
}
.col-sm-pull-5 {
right: 41.66666667%;
}
.col-sm-pull-4 {
right: 33.33333333%;
}
.col-sm-pull-3 {
right: 25%;
}
.col-sm-pull-2 {
right: 16.66666667%;
}
.col-sm-pull-1 {
right: 8.33333333%;
}
.col-sm-pull-0 {
right: auto;
}
.col-sm-push-12 {
left: 100%;
}
.col-sm-push-11 {
left: 91.66666667%;
}
.col-sm-push-10 {
left: 83.33333333%;
}
.col-sm-push-9 {
left: 75%;
}
.col-sm-push-8 {
left: 66.66666667%;
}
.col-sm-push-7 {
left: 58.33333333%;
}
.col-sm-push-6 {
left: 50%;
}
.col-sm-push-5 {
left: 41.66666667%;
}
.col-sm-push-4 {
left: 33.33333333%;
}
.col-sm-push-3 {
left: 25%;
}
.col-sm-push-2 {
left: 16.66666667%;
}
.col-sm-push-1 {
left: 8.33333333%;
}
.col-sm-push-0 {
left: auto;
}
.col-sm-offset-12 {
margin-left: 100%;
}
.col-sm-offset-11 {
margin-left: 91.66666667%;
}
.col-sm-offset-10 {
margin-left: 83.33333333%;
}
.col-sm-offset-9 {
margin-left: 75%;
}
.col-sm-offset-8 {
margin-left: 66.66666667%;
}
.col-sm-offset-7 {
margin-left: 58.33333333%;
}
.col-sm-offset-6 {
margin-left: 50%;
}
.col-sm-offset-5 {
margin-left: 41.66666667%;
}
.col-sm-offset-4 {
margin-left: 33.33333333%;
}
.col-sm-offset-3 {
margin-left: 25%;
}
.col-sm-offset-2 {
margin-left: 16.66666667%;
}
.col-sm-offset-1 {
margin-left: 8.33333333%;
}
.col-sm-offset-0 {
margin-left: 0%;
}
.visible-xs {
display: none !important;
}
.hidden-xs {
display: block !important;
}
table.hidden-xs {
display: table;
}
tr.hidden-xs {
display: table-row !important;
}
th.hidden-xs,
td.hidden-xs {
display: table-cell !important;
}
.hidden-xs.hidden-print {
display: none !important;
}
.hidden-sm {
display: none !important;
}
.visible-sm {
display: block !important;
}
table.visible-sm {
display: table;
}
tr.visible-sm {
display: table-row !important;
}
th.visible-sm,
td.visible-sm {
display: table-cell !important;
}
}
I had the similar problem. I ended up replacing all col-md-* with col-xs-* and it worked like a charm. Here is the exmaple code
<div class="container make-border" id="obaidrehman07">
<div class="row make-border">
<div class="col-md-9 text-center main-title col-md-offset-1">
<h4 class="heading-white"><strong>CONTRACT ACTION REPORT</strong></h4>
</div>
<div class="col-md-0 pull-right"> <img class="img-responsive" src="<?=$base_url;?>assets/office_home/target/dd46a999e4f329f98e5b8df60e21e9ab.png" alt="" style="max-width: 110px;" /> </div>
</div>
</div>
converted to
<div class="container make-border" id="obaidrehman07">
<div class="row make-border">
<div class="col-xs-9 text-center main-title col-xs-offset-1">
<h4 class="heading-white"><strong>CONTRACT ACTION REPORT</strong></h4>
</div>
<div class="col-xs-0 pull-right"> <img class="img-responsive" src="<?=$base_url;?>assets/office_home/target/dd46a999e4f329f98e5b8df60e21e9ab.png" alt="" style="max-width: 110px;" /> </div>
</div>
</div>

Resources