Any best example is there for drag and upload the image in angularjs or angular any version? I didn't find anything in angular.
jsfiddle
I have this example, But my requirement is, I have one + button in a box. If I drag and drop any image on that + button it should upload and one more box should open next to that box. Please see the image.
Like this I need to upload more images. If any fiddle or examples are there, please anyone send me.
// Generated by CoffeeScript 1.6.3
// Couldn't get JSFiddle to work with CoffeeScript as advertised - Link to CoffeeScript Gist: https://gist.github.com/lsiv568/5623361
(function() {
'use strict';
angular.module('reusableThings', []).directive('fileDropzone', function() {
return {
restrict: 'A',
scope: {
file: '=',
fileName: '='
},
link: function(scope, element, attrs) {
var checkSize, isTypeValid, processDragOverOrEnter, validMimeTypes;
processDragOverOrEnter = function(event) {
if (event != null) {
event.preventDefault();
}
event.dataTransfer.effectAllowed = 'copy';
return false;
};
validMimeTypes = attrs.fileDropzone;
checkSize = function(size) {
var _ref;
if (((_ref = attrs.maxFileSize) === (void 0) || _ref === '') || (size / 1024) / 1024 < attrs.maxFileSize) {
return true;
} else {
alert("File must be smaller than " + attrs.maxFileSize + " MB");
return false;
}
};
isTypeValid = function(type) {
if ((validMimeTypes === (void 0) || validMimeTypes === '') || validMimeTypes.indexOf(type) > -1) {
return true;
} else {
alert("Invalid file type. File must be one of following types " + validMimeTypes);
return false;
}
};
element.bind('dragover', processDragOverOrEnter);
element.bind('dragenter', processDragOverOrEnter);
return element.bind('drop', function(event) {
var file, name, reader, size, type;
if (event != null) {
event.preventDefault();
}
reader = new FileReader();
reader.onload = function(evt) {
if (checkSize(size) && isTypeValid(type)) {
return scope.$apply(function() {
scope.file = evt.target.result;
if (angular.isString(scope.fileName)) {
return scope.fileName = name;
}
});
}
};
file = event.dataTransfer.files[0];
name = file.name;
type = file.type;
size = file.size;
reader.readAsDataURL(file);
return false;
});
}
};
});
}).call(this);
(function() {
'use strict';
angular.module('reusableThings').controller('TestCtrl', function($scope) {
$scope.image = null
$scope.imageFileName = ''
});
}).call(this);
.dropzone {
width: 250px;
height: 45px;
border: 1px solid #ccc;
text-align: center;
padding: 30px;
margin: 20px;
font-family: Arial;
position: absolute;
top: 0;
left: 0;
}
.image-container {
width: 312px;
height: 312px;
margin: 20px;
position: absolute;
top: 0;
left: 0;
z-index: 2;
}
.image-container img {
max-width: 100%;
}
.file-name {
font-family: Arial;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="reusableThings" ng-controller="TestCtrl">
<div class="dropzone" file-dropzone="[image/png, image/jpeg, image/gif]" file="image" file-name="imageFileName" data-max-file-size="3">
<span>Drop Image Here</span>
</div>
<div class="image-container" ng-show="image">
<img ng-src={{image}} />
<span class="file-name">{{imageFileName}}</span>
</div>
</div>
Let's agree about some steps:
To support multiple images in the scope, obviously, you should keep an array of images.
We want to re-use the dropzone so after a user will drop an image to it, the image will be next to it and the user could drop another image. So, we don't want to handle the scope, we will parse the file (src and name) and call a callback onDrop with these parameters, and the control itself will handle it.
Please, read my comment in the code
// Generated by CoffeeScript 1.6.3
// Couldn't get JSFiddle to work with CoffeeScript as advertised - Link to CoffeeScript Gist: https://gist.github.com/lsiv568/5623361
(function() {
'use strict';
angular.module('reusableThings', []).directive('fileDropzone', function() {
return {
restrict: 'A',
scope: {
// get only a drop callback which will be called with the image object
image: '='
},
link: function(scope, element, attrs) {
var checkSize, isTypeValid, processDragOverOrEnter, validMimeTypes;
processDragOverOrEnter = function(event) {
if (event != null) {
event.preventDefault();
}
event.dataTransfer.effectAllowed = 'copy';
return false;
};
validMimeTypes = attrs.fileDropzone;
checkSize = function(size) {
var _ref;
if (((_ref = attrs.maxFileSize) === (void 0) || _ref === '') || (size / 1024) / 1024 < attrs.maxFileSize) {
return true;
} else {
alert("File must be smaller than " + attrs.maxFileSize + " MB");
return false;
}
};
isTypeValid = function(type) {
if ((validMimeTypes === (void 0) || validMimeTypes === '') || validMimeTypes.indexOf(type) > -1) {
return true;
} else {
alert("Invalid file type. File must be one of following types " + validMimeTypes);
return false;
}
};
element.bind('dragover', processDragOverOrEnter);
element.bind('dragenter', processDragOverOrEnter);
return element.bind('drop', function(event) {
var file, name, reader, size, type;
if (event != null) {
event.preventDefault();
}
reader = new FileReader();
reader.onload = function(evt) {
if (checkSize(size) && isTypeValid(type)) {
return scope.$apply(function() {
// call the callback with the data
scope.image.image = evt.target.result,
scope.image.imageFileName = name;
});
}
};
file = event.dataTransfer.files[0];
name = file.name;
type = file.type;
size = file.size;
reader.readAsDataURL(file);
return false;
});
}
};
});
}).call(this);
(function() {
'use strict';
angular.module('reusableThings').controller('TestCtrl', function($scope) {
// keep in array instead of variables on the scope
$scope.images = [];
$scope.drop = (image) => {
// console.log(image);
$scope.images.unshift(image);
}
});
}).call(this);
.container {
position: relative;
width: 312px;
height: 312px;
margin: 20px;
}
.dropzone {
border: 1px solid #ccc;
text-align: center;
padding: 30px;
font-family: Arial;
position: absolute;
top: 0;
left: 0;
width: 250px;
height: 45px;
}
.image-container {
width: 100%;
height: 100%;
margin: 20px;
position: absolute;
top: 0;
left: 0;
z-index: 2;
}
.image-container img {
max-width: 100%;
}
.file-name {
font-family: Arial;
}
.button {
width: 50px;
height: 50px;
border-radius: 50%;
text-align: center;
border: 1px solid;
font-size: 25px;
color: #aaa;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="reusableThings" ng-controller="TestCtrl">
<div class="container" ng-repeat="image in images">
<div ng-if="!image.image" class="dropzone" file-dropzone="[image/png, image/jpeg, image/gif]" image="image" data-max-file-size="3">
<span>Drop Image Here</span>
</div>
<div class="image-container" ng-if="image.image">
<img ng-src={{image.image}} />
<span class="file-name">{{image.imageFileName}}</span>
</div>
</div>
<button class="button" ng-click="images.push({})">+</button>
</div>
It could be a little complex change in the line of thought. If anything is not clear, let me know.
Related
i used by javascrip upload multiple file but that files insert different columns.
Why is it that when uploading multiple files with drag drop, they are written in multiple rows instead of one line in the database?
Pictured is a view of the data after inserting it into the database
My php Code is : uploads.php
if(isset($_FILES)){
$upload_dir = "upload/";
$fileName = strtolower(pathinfo($_FILES["files"]["name"], PATHINFO_EXTENSION));
$new_file_name = rand() . '.' . $fileName;
$uploaded_file = $upload_dir.$new_file_name;
if(move_uploaded_file($_FILES['files']['tmp_name'],$uploaded_file)){
}
$content = $_POST['textpost'];
$data = array(
':file' => $new_file_name,
':uploaded_on' => date("Y-m-d H:i:s"),
':content' => $content,
);
$query = "
INSERT INTO images
(name, uploaded_on ,content)
VALUES (:name,:uploaded_on,:content)
";
$statement = $connect->prepare($query);
$statement->execute($data);
}
The Below is the code I originally used. I used these codes as examples from other people’s codes. Then I made some changes myself and added a little functionality. For example, I added a few codes, such as deleting images, separating images and videos, and brought them to the function I needed. But the problem with me now is that When uploading multiple images at the same time as text, All data sent are insert into separate rows, Finally when I select the data from the database the content and images are not select out correctly.My goal is to insert images and text into a single line.
My javascript Code is :index.html
var dropRegion = document.getElementById("drop-region"),
// where images are previewed
imagePreviewRegion = document.getElementById("image-preview");
var file_drag_area = document.getElementById("file_drag_area");
var images_button = document.getElementById("images_btn");
var submit = document.getElementById("submit");
// open file selector when clicked on the drop region
var fakeInput = document.createElement("input");
fakeInput.type = "file";
fakeInput.accept = "video/*,image/*";
fakeInput.multiple = true;
images_button.addEventListener('click', function() {
fakeInput.click();
});
fakeInput.addEventListener("change", function() {
var files = fakeInput.files;
handleFiles(files);
});
function preventDefault(e) {
e.preventDefault();
e.stopPropagation();
}
file_drag_area.addEventListener('dragenter', preventDefault, false)
file_drag_area.addEventListener('dragleave', preventDefault, false)
file_drag_area.addEventListener('dragover', preventDefault, false)
file_drag_area.addEventListener('drop', preventDefault, false)
dropRegion.addEventListener('dragenter', preventDefault, false)
dropRegion.addEventListener('dragleave', preventDefault, false)
dropRegion.addEventListener('dragover', preventDefault, false)
dropRegion.addEventListener('drop', preventDefault, false)
function handleDrop(e) {
var dt = e.dataTransfer,
files = dt.files;
handleFiles(files)
}
dropRegion.addEventListener('drop', handleDrop, false);
file_drag_area.addEventListener('drop', handleDrop, false);
// when drag file the border change to other dotted
file_drag_area.addEventListener("dragenter", function(event) {
if ( event.target.className == "file_drag_area" ) {
event.target.style.border = "3px dotted #79d4ff";
}
});
// when drag file the border display none
file_drag_area.addEventListener("dragleave", function(event) {
if ( event.target.className == "trigger_popup_fricc_wrapper" ) {
event.target.style.border = "";
}
});
file_drag_area.addEventListener("drop", function(event) {
if ( event.target.className == "trigger_popup_fricc_wrapper" ) {
event.target.style.border = "";
}
});
function handleFiles(files) {
for (var i = 0, len = files.length; i < len; i++) {
if (validateImage(files[i]))
previewAnduploadImage(files[i]);
}
}
function validateImage(image) {
// check the type
var validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/jpg','video/mp4', 'video/mkv', 'video/webm'];
var vedioType = validTypes.includes('video/mp4', 'video/mkv', 'video/webm');
if (validTypes.indexOf( image.type ) === -1 && vedioType.indexOf( video.type ) === -1) {
alert("Invalid File Type");
return false;
}
// check the size
var maxSizeInBytes = 10e6; // 10MB
if (image.size > maxSizeInBytes) {
alert("File too large");
return false;
}
return true;
}
function previewAnduploadImage(image) {
var fileType = image.type;
var match = ['image/jpeg', 'image/png', 'image/gif', 'image/jpg','video/mp4', 'video/mkv', 'video/webm'];
if ((fileType == match[0]) || (fileType == match[1]) || (fileType == match[2]) || (fileType == match[3])) {
var imgView = document.createElement("div");
imgView.className = "image-view";
imagePreviewRegion.appendChild(imgView);
var icon = document.createElement("div")
icon.className = "image-icon";
//icon.setAttribute("onclick", "remove_image()");
imgView.appendChild(icon);
var remove = document.createElement("img")
remove.className = "img_class";
remove.setAttribute("height", "20px");
remove.setAttribute("width", "20px");
remove.setAttribute("src", "../../assets/images/cancel.png");
icon.appendChild(remove);
// previewing image
var img = document.createElement("img");
img.className = "remove_input";
img.setAttribute("height", "200px");
img.setAttribute("width", "300px");
imgView.appendChild(img);
var reader = new FileReader();
reader.onload = function(e) {
img.src = e.target.result;
}
reader.readAsDataURL(image);
}else if ((fileType == match[4]) || (fileType == match[5]) || (fileType == match[6])) {
// show video
var video = document.createElement("video")
video.classList.add("obj");
video.file = image;
video.className = "preview_vedio";
video.controls = true;
video.autoplay = true;
video.setAttribute("width", "500");
video.setAttribute("height", "300");
//icon.setAttribute("onclick", "remove_image()");
imagePreviewRegion.appendChild(video);
var reader = new FileReader();
reader.onload = function(e) {
video.src = e.target.result;
}
reader.readAsDataURL(image);
}
var formData = new FormData();
formData.append('image', image);
formData.append('action', 'files');
//formData.append('vedio', vediofiles);
$("#drop-region").css("display", "block");
$("#drop-region").slideDown("slow")
addEventListener('submit', (event) => {
var uploadLocation = 'uploads.php';
var ajax = new XMLHttpRequest();
ajax.open("POST", uploadLocation, true);
ajax.onreadystatechange = function(e) {
if (ajax.readyState === 4) {
if (ajax.status === 200) {
//var myObj = JSON.parse(this.responseText);
} else {
// error!
}
}
}
ajax.upload.onprogress = function(e) {
// change progress
// (reduce the width of overlay)
var perc = (e.loaded / e.total * 100) || 100,
width = 100 - perc;
overlay.style.width = width;
}
ajax.send(formData);
});
}
textarea
{
width: 100%;
margin: 0px;
padding: .2em;
border: none;
outline-width: 0;
text-align: start;
unicode-bidi: plaintext;
font-weight: 400;
cursor: text;
background-color: #ffffff;
overflow:hidden;
resize: none;
font-size: 18px;
}
input#submit {
width: 100%;
color: white;
background: #79d4ff;
font-size: 18px;
border: none;
border-radius: 8px;
outline-width: 0;
line-height: 2;
}
input#submit:hover
{
background: #3da0f5;
}
input#submit:focus{width: 98%;}
#drop-region {
background-color: #fff;
border-radius:20px;
box-shadow:0 0 35px rgba(0,0,0,0.05);
padding:60px 40px;
text-align: center;
cursor:pointer;
transition:.3s;
display:none;
}
#drop-region:hover {
box-shadow:0 0 45px rgba(0,0,0,0.1);
}
#image-preview {
margin-top:20px;
}
#image-preview .image-view {
display: inline-block;
position:relative;
margin-right: 13px;
margin-bottom: 13px;
}
#image-preview .image-view img {
max-width: 300px;
max-height: 300px;
transform: scale(.9);
transition: .4s;
}
#image-preview .overlay {
position: absolute;
width: 100%;
height: 100%;
top: 0;
right: 0;
z-index: 2;
background: rgba(255,255,255,0.5);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<form id="fupForm"enctype="multipart/form-data" accept-charset="utf-8">
<textarea style="direction:ltr;" name="post_content" class="file_drag_area" id="file_drag_area" maxlength="1000" placeholder="Greate Post..." ></textarea>
<div id="drop-region">
<div id="image-preview">
</div>
</div>
<button type="button" class="emoji_button" id="images_btn"><img src="../../assets/images/image.png" width="25px" height="25px" ></button>
<input type="submit" name="submit" id="submit" class="btn btn-primary" value="Send Post" />
</form>
For your case, when you drag and drop the files (file upload), you will trigger the following function:
function handleFiles(files) {
for (var i = 0, len = files.length; i < len; i++) {
if (validateImage(files[i]))
previewAnduploadImage(files[i]);
}
}
It is obvious that this "handleFiles" function will process the files one by one. Hence the php is called ONCE for every file uploaded --- and that explains why the files are saved into separate records.
The above should explain what you encounter. So if you want to save the data into a single record, you need to amend the above codes
I have two divs that are overlapping using Bootstrap's 2.3.2 grid system when the browser is resized. The result looks like the following after you downsize the browser window:
I don't want the two divs to ever overlap. If it comes to a point where they cannot fit on the same row, the second div should fall below the first, which the responsive layout is already doing. My problem is those weird few pixels where they overlap.
I'm using Bootstrap's tags input library here.
The HTML:
<div class="container-fluid">
<div class="row-fluid">
<div class="span12" style="border: 1px solid black;">
<div class="span1 offset4" style="border: 1px solid blue;">
<select id="container" multiple data-role="tagsinput"> </select>
</div>
<div class="span2 offset2" style="border: 1px solid red;">
<h4>
Second Div
</h4>
<div id="second_div">
<p>
testing...... 1 ... 2 ... 3...
</p>
</div>
</div>
</div>
</div>
</div>
JavaScript to populate some dummy data:
$(document).ready(function() {
$('select').tagsinput('add', 'Foo');
$('select').tagsinput('add', 'Bar');
$('select').tagsinput('add', 'This is a very very very very very very long filter');
});
And my CSS:
.small {
font-size: 10px;
}
.tag {
/*height: 25px;*/
vertical-align: middle;
line-height: 25px;
/*width: 75%;*/
word-wrap: break-word;
white-space: normal;
}
ul {
list-style-type: none;
}
.top-margin {
margin-top: 5px;
}
.bootstrap-tagsinput {
width: 200px;
max-width: none;
}
I have a jsfiddle which demos the problem here: https://jsfiddle.net/brseyg6c/
The problem is not Bootstrap but instead this declaration:
.bootstrap-tagsinput {
width: 200px;
max-width: none;
}
You're fixing the width of the tag container while it's inside a dynamically sized Bootstrap column.
Instead, have the container fill the width of the column:
.bootstrap-tagsinput {
box-sizing: border-box;
width: 100%;
}
You might want to increase the width of the left column (change span1 to be larger) and remove the offset from the right column.
I have two idea for you.
Trick 1: Can you make it like this with out media query just replace the off-set2 and make offset3. and add this into class .bootstrap-tagsinput .tag white-space:pre-wrap; .otherwise in snippet large tag came out of the box. Trick 2 Fiddle
.bootstrap-tagsinput {
background-color: #fff;
border: 1px solid #ccc;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
display: inline-block;
padding: 4px 6px;
color: #555;
vertical-align: middle;
border-radius: 4px;
max-width: 100%;
line-height: 22px;
cursor: text;
}
.bootstrap-tagsinput input {
border: none;
box-shadow: none;
outline: none;
background-color: transparent;
padding: 0 6px;
margin: 0;
width: auto;
max-width: inherit;
}
.bootstrap-tagsinput.form-control input::-moz-placeholder {
color: #777;
opacity: 1;
}
.bootstrap-tagsinput.form-control input:-ms-input-placeholder {
color: #777;
}
.bootstrap-tagsinput.form-control input::-webkit-input-placeholder {
color: #777;
}
.bootstrap-tagsinput input:focus {
border: none;
box-shadow: none;
}
.bootstrap-tagsinput .tag {
margin-top: 5px;
/* custom */
margin-right: 2px;
color: white;
white-space:pre-wrap;
}
.bootstrap-tagsinput .tag [data-role="remove"] {
margin-left: 8px;
cursor: pointer;
}
.bootstrap-tagsinput .tag [data-role="remove"]:after {
content: "x";
padding: 0px 2px;
}
.bootstrap-tagsinput .tag [data-role="remove"]:hover {
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
}
.bootstrap-tagsinput .tag [data-role="remove"]:hover:active {
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
body {
padding: 20px;
}
h1, h2, h3, h4, h5, h6 {
text-shadow: 1px 1px 1px #fff;
color: #000;
font-weight: bold;
line-height: 1.5;
margin: 0;
}
pre {
border-radius: 3px;
overflow-x: scroll;
}
p {
color: #000;
}
p a {
color: #990033;
text-decoration: none;
transition: all .25s;
padding: 3px;
}
p a:hover {
color: #fff;
background: #990033;
transition: all .25s;
}
ul,
ol {
margin-top: -10px;
}
.white {
color: white;
}
.small {
font-size: 10px;
}
.tag {
/*height: 25px;*/
vertical-align: middle;
line-height: 25px;
/*width: 75%;*/
word-wrap: break-word;
white-space: normal;
}
ul {
list-style-type: none;
}
.top-margin {
margin-top: 5px;
}
.bootstrap-tagsinput {
width: 200px;
max-width: none;
}
<head>
<script src="https://code.jquery.com/jquery-1.12.4.min.js" ></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js" ></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet" type="text/css" />
<link href="https://netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12" style="border: 1px solid black;">
<div class="span1 offset4" style="border: 1px solid blue;">
<select id="container" multiple data-role="tagsinput"> </select>
</div>
<div class="span2 offset3" style="border: 1px solid red;">
<h4>
Second Div
</h4>
<div id="event-log">
<p>
testing...... 1 ... 2 ... 3...
</p>
</div>
</div>
</div>
</div>
</div>
</body>
(function($) {
"use strict";
var defaultOptions = {
tagClass: function(item) {
return 'label label-info';
},
itemValue: function(item) {
return item ? item.toString() : item;
},
itemText: function(item) {
return this.itemValue(item);
},
itemTitle: function(item) {
return null;
},
freeInput: true,
addOnBlur: true,
maxTags: undefined,
maxChars: undefined,
confirmKeys: [13, 44],
delimiter: ',',
delimiterRegex: null,
cancelConfirmKeysOnEmpty: true,
onTagExists: function(item, $tag) {
$tag.hide().fadeIn();
},
trimValue: false,
allowDuplicates: false
};
/**
* Constructor function
*/
function TagsInput(element, options) {
this.itemsArray = [];
this.$element = $(element);
this.$element.hide();
this.isSelect = (element.tagName === 'SELECT');
this.multiple = (this.isSelect && element.hasAttribute('multiple'));
this.objectItems = options && options.itemValue;
this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : '';
this.inputSize = Math.max(1, this.placeholderText.length);
this.$container = $('<div class="bootstrap-tagsinput"></div>');
this.$input = $('<input type="text" placeholder="' + this.placeholderText + '"/>').appendTo(this.$container);
this.$element.before(this.$container);
this.build(options);
}
TagsInput.prototype = {
constructor: TagsInput,
/**
* Adds the given item as a new tag. Pass true to dontPushVal to prevent
* updating the elements val()
*/
add: function(item, dontPushVal, options) {
var self = this;
if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)
return;
// Ignore falsey values, except false
if (item !== false && !item)
return;
// Trim value
if (typeof item === "string" && self.options.trimValue) {
item = $.trim(item);
}
// Throw an error when trying to add an object while the itemValue option was not set
if (typeof item === "object" && !self.objectItems)
throw ("Can't add objects when itemValue option is not set");
// Ignore strings only containg whitespace
if (item.toString().match(/^\s*$/))
return;
// If SELECT but not multiple, remove current tag
if (self.isSelect && !self.multiple && self.itemsArray.length > 0)
self.remove(self.itemsArray[0]);
if (typeof item === "string" && this.$element[0].tagName === 'INPUT') {
var delimiter = (self.options.delimiterRegex) ? self.options.delimiterRegex : self.options.delimiter;
var items = item.split(delimiter);
if (items.length > 1) {
for (var i = 0; i < items.length; i++) {
this.add(items[i], true);
}
if (!dontPushVal)
self.pushVal();
return;
}
}
var itemValue = self.options.itemValue(item),
itemText = self.options.itemText(item),
tagClass = self.options.tagClass(item),
itemTitle = self.options.itemTitle(item);
// Ignore items allready added
var existing = $.grep(self.itemsArray, function(item) {
return self.options.itemValue(item) === itemValue;
})[0];
if (existing && !self.options.allowDuplicates) {
// Invoke onTagExists
if (self.options.onTagExists) {
var $existingTag = $(".tag", self.$container).filter(function() {
return $(this).data("item") === existing;
});
self.options.onTagExists(item, $existingTag);
}
return;
}
// if length greater than limit
if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)
return;
// raise beforeItemAdd arg
var beforeItemAddEvent = $.Event('beforeItemAdd', {
item: item,
cancel: false,
options: options
});
self.$element.trigger(beforeItemAddEvent);
if (beforeItemAddEvent.cancel)
return;
// register item in internal array and map
self.itemsArray.push(item);
// add a tag element
var $tag = $('<span class="tag ' + htmlEncode(tagClass) + (itemTitle !== null ? ('" title="' + itemTitle) : '') + '">' + htmlEncode(itemText) + '<span data-role="remove"></span></span>');
$tag.data('item', item);
self.findInputWrapper().before($tag);
$tag.after(' ');
// add <option /> if item represents a value not present in one of the <select />'s options
if (self.isSelect && !$('option[value="' + encodeURIComponent(itemValue) + '"]', self.$element)[0]) {
var $option = $('<option selected>' + htmlEncode(itemText) + '</option>');
$option.data('item', item);
$option.attr('value', itemValue);
self.$element.append($option);
}
if (!dontPushVal)
self.pushVal();
// Add class when reached maxTags
if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)
self.$container.addClass('bootstrap-tagsinput-max');
self.$element.trigger($.Event('itemAdded', {
item: item,
options: options
}));
},
/**
* Removes the given item. Pass true to dontPushVal to prevent updating the
* elements val()
*/
remove: function(item, dontPushVal, options) {
var self = this;
if (self.objectItems) {
if (typeof item === "object")
item = $.grep(self.itemsArray, function(other) {
return self.options.itemValue(other) == self.options.itemValue(item);
});
else
item = $.grep(self.itemsArray, function(other) {
return self.options.itemValue(other) == item;
});
item = item[item.length - 1];
}
if (item) {
var beforeItemRemoveEvent = $.Event('beforeItemRemove', {
item: item,
cancel: false,
options: options
});
self.$element.trigger(beforeItemRemoveEvent);
if (beforeItemRemoveEvent.cancel)
return;
$('.tag', self.$container).filter(function() {
return $(this).data('item') === item;
}).remove();
$('option', self.$element).filter(function() {
return $(this).data('item') === item;
}).remove();
if ($.inArray(item, self.itemsArray) !== -1)
self.itemsArray.splice($.inArray(item, self.itemsArray), 1);
}
if (!dontPushVal)
self.pushVal();
// Remove class when reached maxTags
if (self.options.maxTags > self.itemsArray.length)
self.$container.removeClass('bootstrap-tagsinput-max');
self.$element.trigger($.Event('itemRemoved', {
item: item,
options: options
}));
},
/**
* Removes all items
*/
removeAll: function() {
var self = this;
$('.tag', self.$container).remove();
$('option', self.$element).remove();
while (self.itemsArray.length > 0)
self.itemsArray.pop();
self.pushVal();
},
/**
* Refreshes the tags so they match the text/value of their corresponding
* item.
*/
refresh: function() {
var self = this;
$('.tag', self.$container).each(function() {
var $tag = $(this),
item = $tag.data('item'),
itemValue = self.options.itemValue(item),
itemText = self.options.itemText(item),
tagClass = self.options.tagClass(item);
// Update tag's class and inner text
$tag.attr('class', null);
$tag.addClass('tag ' + htmlEncode(tagClass));
$tag.contents().filter(function() {
return this.nodeType == 3;
})[0].nodeValue = htmlEncode(itemText);
if (self.isSelect) {
var option = $('option', self.$element).filter(function() {
return $(this).data('item') === item;
});
option.attr('value', itemValue);
}
});
},
/**
* Returns the items added as tags
*/
items: function() {
return this.itemsArray;
},
/**
* Assembly value by retrieving the value of each item, and set it on the
* element.
*/
pushVal: function() {
var self = this,
val = $.map(self.items(), function(item) {
return self.options.itemValue(item).toString();
});
self.$element.val(val, true).trigger('change');
},
/**
* Initializes the tags input behaviour on the element
*/
build: function(options) {
var self = this;
self.options = $.extend({}, defaultOptions, options);
// When itemValue is set, freeInput should always be false
if (self.objectItems)
self.options.freeInput = false;
makeOptionItemFunction(self.options, 'itemValue');
makeOptionItemFunction(self.options, 'itemText');
makeOptionFunction(self.options, 'tagClass');
// Typeahead Bootstrap version 2.3.2
if (self.options.typeahead) {
var typeahead = self.options.typeahead || {};
makeOptionFunction(typeahead, 'source');
self.$input.typeahead($.extend({}, typeahead, {
source: function(query, process) {
function processItems(items) {
var texts = [];
for (var i = 0; i < items.length; i++) {
var text = self.options.itemText(items[i]);
map[text] = items[i];
texts.push(text);
}
process(texts);
}
this.map = {};
var map = this.map,
data = typeahead.source(query);
if ($.isFunction(data.success)) {
// support for Angular callbacks
data.success(processItems);
} else if ($.isFunction(data.then)) {
// support for Angular promises
data.then(processItems);
} else {
// support for functions and jquery promises
$.when(data)
.then(processItems);
}
},
updater: function(text) {
self.add(this.map[text]);
return this.map[text];
},
matcher: function(text) {
return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
},
sorter: function(texts) {
return texts.sort();
},
highlighter: function(text) {
var regex = new RegExp('(' + this.query + ')', 'gi');
return text.replace(regex, "<strong>$1</strong>");
}
}));
}
// typeahead.js
if (self.options.typeaheadjs) {
var typeaheadConfig = null;
var typeaheadDatasets = {};
// Determine if main configurations were passed or simply a dataset
var typeaheadjs = self.options.typeaheadjs;
if ($.isArray(typeaheadjs)) {
typeaheadConfig = typeaheadjs[0];
typeaheadDatasets = typeaheadjs[1];
} else {
typeaheadDatasets = typeaheadjs;
}
self.$input.typeahead(typeaheadConfig, typeaheadDatasets).on('typeahead:selected', $.proxy(function(obj, datum) {
if (typeaheadDatasets.valueKey)
self.add(datum[typeaheadDatasets.valueKey]);
else
self.add(datum);
self.$input.typeahead('val', '');
}, self));
}
self.$container.on('click', $.proxy(function(event) {
if (!self.$element.attr('disabled')) {
self.$input.removeAttr('disabled');
}
self.$input.focus();
}, self));
if (self.options.addOnBlur && self.options.freeInput) {
self.$input.on('focusout', $.proxy(function(event) {
// HACK: only process on focusout when no typeahead opened, to
// avoid adding the typeahead text as tag
if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) {
self.add(self.$input.val());
self.$input.val('');
}
}, self));
}
self.$container.on('keydown', 'input', $.proxy(function(event) {
var $input = $(event.target),
$inputWrapper = self.findInputWrapper();
if (self.$element.attr('disabled')) {
self.$input.attr('disabled', 'disabled');
return;
}
switch (event.which) {
// BACKSPACE
case 8:
if (doGetCaretPosition($input[0]) === 0) {
var prev = $inputWrapper.prev();
if (prev.length) {
self.remove(prev.data('item'));
}
}
break;
// DELETE
case 46:
if (doGetCaretPosition($input[0]) === 0) {
var next = $inputWrapper.next();
if (next.length) {
self.remove(next.data('item'));
}
}
break;
// LEFT ARROW
case 37:
// Try to move the input before the previous tag
var $prevTag = $inputWrapper.prev();
if ($input.val().length === 0 && $prevTag[0]) {
$prevTag.before($inputWrapper);
$input.focus();
}
break;
// RIGHT ARROW
case 39:
// Try to move the input after the next tag
var $nextTag = $inputWrapper.next();
if ($input.val().length === 0 && $nextTag[0]) {
$nextTag.after($inputWrapper);
$input.focus();
}
break;
default:
// ignore
}
// Reset internal input's size
var textLength = $input.val().length,
wordSpace = Math.ceil(textLength / 5),
size = textLength + wordSpace + 1;
$input.attr('size', Math.max(this.inputSize, $input.val().length));
}, self));
self.$container.on('keypress', 'input', $.proxy(function(event) {
var $input = $(event.target);
if (self.$element.attr('disabled')) {
self.$input.attr('disabled', 'disabled');
return;
}
var text = $input.val(),
maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars;
if (self.options.freeInput && (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached)) {
// Only attempt to add a tag if there is data in the field
if (text.length !== 0) {
self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);
$input.val('');
}
// If the field is empty, let the event triggered fire as usual
if (self.options.cancelConfirmKeysOnEmpty === false) {
event.preventDefault();
}
}
// Reset internal input's size
var textLength = $input.val().length,
wordSpace = Math.ceil(textLength / 5),
size = textLength + wordSpace + 1;
$input.attr('size', Math.max(this.inputSize, $input.val().length));
}, self));
// Remove icon clicked
self.$container.on('click', '[data-role=remove]', $.proxy(function(event) {
if (self.$element.attr('disabled')) {
return;
}
self.remove($(event.target).closest('.tag').data('item'));
}, self));
// Only add existing value as tags when using strings as tags
if (self.options.itemValue === defaultOptions.itemValue) {
if (self.$element[0].tagName === 'INPUT') {
self.add(self.$element.val());
} else {
$('option', self.$element).each(function() {
self.add($(this).attr('value'), true);
});
}
}
},
/**
* Removes all tagsinput behaviour and unregsiter all event handlers
*/
destroy: function() {
var self = this;
// Unbind events
self.$container.off('keypress', 'input');
self.$container.off('click', '[role=remove]');
self.$container.remove();
self.$element.removeData('tagsinput');
self.$element.show();
},
/**
* Sets focus on the tagsinput
*/
focus: function() {
this.$input.focus();
},
/**
* Returns the internal input element
*/
input: function() {
return this.$input;
},
/**
* Returns the element which is wrapped around the internal input. This
* is normally the $container, but typeahead.js moves the $input element.
*/
findInputWrapper: function() {
var elt = this.$input[0],
container = this.$container[0];
while (elt && elt.parentNode !== container)
elt = elt.parentNode;
return $(elt);
}
};
/**
* Register JQuery plugin
*/
$.fn.tagsinput = function(arg1, arg2, arg3) {
var results = [];
this.each(function() {
var tagsinput = $(this).data('tagsinput');
// Initialize a new tags input
if (!tagsinput) {
tagsinput = new TagsInput(this, arg1);
$(this).data('tagsinput', tagsinput);
results.push(tagsinput);
if (this.tagName === 'SELECT') {
$('option', $(this)).attr('selected', 'selected');
}
// Init tags from $(this).val()
$(this).val($(this).val());
} else if (!arg1 && !arg2) {
// tagsinput already exists
// no function, trying to init
results.push(tagsinput);
} else if (tagsinput[arg1] !== undefined) {
// Invoke function on existing tags input
if (tagsinput[arg1].length === 3 && arg3 !== undefined) {
var retVal = tagsinput[arg1](arg2, null, arg3);
} else {
var retVal = tagsinput[arg1](arg2);
}
if (retVal !== undefined)
results.push(retVal);
}
});
if (typeof arg1 == 'string') {
// Return the results from the invoked function calls
return results.length > 1 ? results : results[0];
} else {
return results;
}
};
$.fn.tagsinput.Constructor = TagsInput;
/**
* Most options support both a string or number as well as a function as
* option value. This function makes sure that the option with the given
* key in the given options is wrapped in a function
*/
function makeOptionItemFunction(options, key) {
if (typeof options[key] !== 'function') {
var propertyName = options[key];
options[key] = function(item) {
return item[propertyName];
};
}
}
function makeOptionFunction(options, key) {
if (typeof options[key] !== 'function') {
var value = options[key];
options[key] = function() {
return value;
};
}
}
/**
* HtmlEncodes the given value
*/
var htmlEncodeContainer = $('<div />');
function htmlEncode(value) {
if (value) {
return htmlEncodeContainer.text(value).html();
} else {
return '';
}
}
/**
* Returns the position of the caret in the given input field
* http://flightschool.acylt.com/devnotes/caret-position-woes/
*/
function doGetCaretPosition(oField) {
var iCaretPos = 0;
if (document.selection) {
oField.focus();
var oSel = document.selection.createRange();
oSel.moveStart('character', -oField.value.length);
iCaretPos = oSel.text.length;
} else if (oField.selectionStart || oField.selectionStart == '0') {
iCaretPos = oField.selectionStart;
}
return (iCaretPos);
}
/**
* Returns boolean indicates whether user has pressed an expected key combination.
* #param object keyPressEvent: JavaScript event object, refer
* http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
* #param object lookupList: expected key combinations, as in:
* [13, {which: 188, shiftKey: true}]
*/
function keyCombinationInList(keyPressEvent, lookupList) {
var found = false;
$.each(lookupList, function(index, keyCombination) {
if (typeof(keyCombination) === 'number' && keyPressEvent.which === keyCombination) {
found = true;
return false;
}
if (keyPressEvent.which === keyCombination.which) {
var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey,
shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey,
ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey;
if (alt && shift && ctrl) {
found = true;
return false;
}
}
});
return found;
}
/**
* Initialize tagsinput behaviour on inputs and selects which have
* data-role=tagsinput
*/
$(function() {
$("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
});
})(window.jQuery);
$(document).ready(function() {
$('select').tagsinput('add', 'Another Model');
$('select').tagsinput('add', 'TestCam7');
$('select').tagsinput('add', 'TestCam4');
$('select').tagsinput('add', 'Profanity');
$('select').tagsinput('add', 'An Anddress');
$('select').tagsinput('add', 'This is a very very very very very very long filter');
});
Trick's 2: Using this 2 media query only you can achieve the same output. Trick's 2 fiddle
#media (min-width: 1076px) and (min-width: 979px) {
.row-fluid .span2 {
margin-left: 15.5%;
}
}
#media (max-width: 979px) and (min-width: 768px) {
.row-fluid .span2 {
margin-left: 25.5%;
}
}
In my AngularJS application I am attempting to add a Facebook Native Web Ad to one of my views. I followed the steps outlined in the documentation to generate the necessary HTML snippet and added this to my view.
My application is using ui-router to resolve routes. When visiting the route/view containing this code snippet the FB ad is not displayed and neither of the callbacks are invoked (onAdLoaded or onAdError).
Facebook Generated HTML Snippet (added to view):
<div style="display:none; position: relative;">
<iframe style="display:none;"></iframe>
<script type="text/javascript">
var data = {
placementid: 'xxxxxxxxxxx_xxxxxxxx',
format: 'native',
testmode: true,
onAdLoaded: function (element) {
console.log('Audience Network ad loaded');
element.style.display = 'block';
},
onAdError: function (errorCode, errorMessage) {
console.log('Audience Network error (' + errorCode + ') ' + errorMessage);
}
};
(function (w, l, d, t) {
var a = t();
var b = d.currentScript || (function () {
var c = d.getElementsByTagName('script');
return c[c.length - 1];
})();
var e = b.parentElement;
e.dataset.placementid = data.placementid;
var f = function (v) {
try {
return v.document.referrer;
} catch (e) {
}
return '';
};
var g = function (h) {
var i = h.indexOf('/', h.indexOf('://') + 3);
if (i === -1) {
return h;
}
return h.substring(0, i);
};
var j = [l.href];
var k = false;
var m = false;
if (w !== w.parent) {
var n;
var o = w;
while (o !== n) {
var h;
try {
m = m || (o.$sf && o.$sf.ext);
h = o.location.href;
} catch (e) {
k = true;
}
j.push(h || f(n));
n = o;
o = o.parent;
}
}
var p = l.ancestorOrigins;
if (p) {
if (p.length > 0) {
data.domain = p[p.length - 1];
} else {
data.domain = g(j[j.length - 1]);
}
}
data.url = j[j.length - 1];
data.channel = g(j[0]);
data.width = screen.width;
data.height = screen.height;
data.pixelratio = w.devicePixelRatio;
data.placementindex = w.ADNW && w.ADNW.Ads ? w.ADNW.Ads.length : 0;
data.crossdomain = k;
data.safeframe = !!m;
var q = {};
q.iframe = e.firstElementChild;
var r = 'https://www.facebook.com/audiencenetwork/web/?sdk=5.3';
for (var s in data) {
q[s] = data[s];
if (typeof(data[s]) !== 'function') {
r += '&' + s + '=' + encodeURIComponent(data[s]);
}
}
q.iframe.src = r;
q.tagJsInitTime = a;
q.rootElement = e;
q.events = [];
w.addEventListener('message', function (u) {
if (u.source !== q.iframe.contentWindow) {
return;
}
u.data.receivedTimestamp = t();
if (this.sdkEventHandler) {
this.sdkEventHandler(u.data);
} else {
this.events.push(u.data);
}
}.bind(q), false);
q.tagJsIframeAppendedTime = t();
w.ADNW = w.ADNW || {};
w.ADNW.Ads = w.ADNW.Ads || [];
w.ADNW.Ads.push(q);
w.ADNW.init && w.ADNW.init(q);
})(window, location, document, Date.now || function () {
return +new Date;
});
</script>
<script type="text/javascript" src="https://connect.facebook.net/en_US/fbadnw.js" async></script>
<style>
.thirdPartyRoot {
background-color: white;
color: #444;
border: 1px solid #ccc;
border-left: 0;
border-right: 0;
font-family: sans-serif;
font-size: 14px;
line-height: 16px;
width: 320px;
text-align: left;
position: relative;
}
.thirdPartyMediaClass {
width: 320px;
height: 168px;
margin: 12px 0;
}
.thirdPartySubtitleClass {
font-size: 18px;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
height: 16px;
-webkit-box-orient: vertical;
}
.thirdPartyTitleClass {
padding-right: 12px;
line-height: 18px;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
height: 36px;
-webkit-box-orient: vertical;
}
.thirdPartyCallToActionClass {
background-color: #416BC4;
color: white;
border-radius: 4px;
padding: 6px 20px;
float: right;
text-align: center;
text-transform: uppercase;
font-size: 11px;
}
.fbDefaultNativeAdWrapper {
font-size: 12px;
line-height: 14px;
margin: 12px 0;
height: 36px;
vertical-align: top;
}
</style>
<div class="thirdPartyRoot">
<a class="fbAdLink">
<div class="fbAdMedia thirdPartyMediaClass"></div>
<div class="fbAdSubtitle thirdPartySubtitleClass"></div>
<div class="fbDefaultNativeAdWrapper">
<div class="fbAdCallToAction thirdPartyCallToActionClass"></div>
<div class="fbAdTitle thirdPartyTitleClass"></div>
</div>
</a>
</div>
</div>
I noticed that the Facebook Audience Network JS is loaded asynchronously and suspected that I might have a race condition causing the issue.
<script type="text/javascript" src="https://connect.facebook.net/en_US/fbadnw.js" async></script>
To test this, I've moved the FB code snippet out of my view and into my SPA index.html. The ad appears as expected. What callback does the fbadnw.js script call once the script is loaded? Is the closure within the FB generated code being invoked before fbadnw.js is loaded perhaps?
index.html (works)
<!DOCTYPE html>
<html ng-app="kcl-app">
<head>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title ng-bind="pageTitle"></title>
</head>
<body>
<!-- ui-router view -->
<div ui-view></div>
<!-- FB Begin -->
<div class="fb-native">
<div style="display:none; position: relative;">
<iframe style="display:none;"></iframe>
<script type="text/javascript">
var data = {
placementid: 'xxxxxxxxxxx_xxxxxxxx',
format: 'native',
testmode: true,
onAdLoaded: function (element) {
console.log('Audience Network ad loaded');
element.style.display = 'block';
},
onAdError: function (errorCode, errorMessage) {
console.log('Audience Network error (' + errorCode + ') ' + errorMessage);
}
};
(function (w, l, d, t) {
var a = t();
var b = d.currentScript || (function () {
var c = d.getElementsByTagName('script');
return c[c.length - 1];
})();
var e = b.parentElement;
e.dataset.placementid = data.placementid;
var f = function (v) {
try {
return v.document.referrer;
} catch (e) {
}
return '';
};
var g = function (h) {
var i = h.indexOf('/', h.indexOf('://') + 3);
if (i === -1) {
return h;
}
return h.substring(0, i);
};
var j = [l.href];
var k = false;
var m = false;
if (w !== w.parent) {
var n;
var o = w;
while (o !== n) {
var h;
try {
m = m || (o.$sf && o.$sf.ext);
h = o.location.href;
} catch (e) {
k = true;
}
j.push(h || f(n));
n = o;
o = o.parent;
}
}
var p = l.ancestorOrigins;
if (p) {
if (p.length > 0) {
data.domain = p[p.length - 1];
} else {
data.domain = g(j[j.length - 1]);
}
}
data.url = j[j.length - 1];
data.channel = g(j[0]);
data.width = screen.width;
data.height = screen.height;
data.pixelratio = w.devicePixelRatio;
data.placementindex = w.ADNW && w.ADNW.Ads ? w.ADNW.Ads.length : 0;
data.crossdomain = k;
data.safeframe = !!m;
var q = {};
q.iframe = e.firstElementChild;
var r = 'https://www.facebook.com/audiencenetwork/web/?sdk=5.3';
for (var s in data) {
q[s] = data[s];
if (typeof(data[s]) !== 'function') {
r += '&' + s + '=' + encodeURIComponent(data[s]);
}
}
q.iframe.src = r;
q.tagJsInitTime = a;
q.rootElement = e;
q.events = [];
w.addEventListener('message', function (u) {
if (u.source !== q.iframe.contentWindow) {
return;
}
u.data.receivedTimestamp = t();
if (this.sdkEventHandler) {
this.sdkEventHandler(u.data);
} else {
this.events.push(u.data);
}
}.bind(q), false);
q.tagJsIframeAppendedTime = t();
w.ADNW = w.ADNW || {};
w.ADNW.Ads = w.ADNW.Ads || [];
w.ADNW.Ads.push(q);
w.ADNW.init && w.ADNW.init(q);
})(window, location, document, Date.now || function () {
return +new Date;
});
</script>
<script type="text/javascript" src="https://connect.facebook.net/en_US/fbadnw.js" async></script>
<style>
.thirdPartyRoot {
background-color: white;
color: #444;
border: 1px solid #ccc;
border-left: 0;
border-right: 0;
font-family: sans-serif;
font-size: 14px;
line-height: 16px;
width: 320px;
text-align: left;
position: relative;
}
.thirdPartyMediaClass {
width: 320px;
height: 168px;
margin: 12px 0;
}
.thirdPartySubtitleClass {
font-size: 18px;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
height: 16px;
-webkit-box-orient: vertical;
}
.thirdPartyTitleClass {
padding-right: 12px;
line-height: 18px;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
height: 36px;
-webkit-box-orient: vertical;
}
.thirdPartyCallToActionClass {
background-color: #416BC4;
color: white;
border-radius: 4px;
padding: 6px 20px;
float: right;
text-align: center;
text-transform: uppercase;
font-size: 11px;
}
.fbDefaultNativeAdWrapper {
font-size: 12px;
line-height: 14px;
margin: 12px 0;
height: 36px;
vertical-align: top;
}
</style>
<div class="thirdPartyRoot">
<a class="fbAdLink">
<div class="fbAdMedia thirdPartyMediaClass"></div>
<div class="fbAdSubtitle thirdPartySubtitleClass"></div>
<div class="fbDefaultNativeAdWrapper">
<div class="fbAdCallToAction thirdPartyCallToActionClass"></div>
<div class="fbAdTitle thirdPartyTitleClass"></div>
</div>
</a>
</div>
</div>
</div>
<!-- FB End -->
</body>
</html>
I was able to resolve this issue by editing the boilerplate code provided by FB. In a nutshell, the provided closure (minified) attempts to:
Locate the iframe element where the Ad will be rendered and set it's
src and other attributes.
Attach an event handler to listen for post messages "message".
Initialize the Ad with FB Audience Network (ADNW.init())
My problem was with the assumptions this code makes in Step 1.
var b = d.currentScript || (function() {
var c = d.getElementsByTagName('script');
return c[c.length - 1];
})();
var e = b.parentElement;
The above code is attempting to locate this DIV.
<div style="display:none; position: relative;">
It does this by first trying to locate the parent of the last script element on the page. This is brittle and, in my case, did not work. The last script element added in my document was not the one this code expected.
I modified this code to explicitly select the correct element by ID.
Added an ID to containing DIV:
<div id="fb-ad-container" style="display:none; position: relative;">
Simplify the DOM parsing code (step 1) to select this DIV by ID:
var e = d.getElementById("fb-ad-container");
By selecting the correct element by ID I was able to alleviate the need to locate the current script element. The rest of the script ran as expected.
I want to upload folder to server by d'n'd via AJAX. But already, I have troubles with upload files.
I use e.dataTransfer.items and webkitGetAsEntry() to check - is it file or folder?
If it's file, in function traverseFileTree I get file, but I can't append it to formData.
If I use e.dataTransfer.files, I don't know what is it. File or Folder because webkitGetAsEntry() get error.
What I do wrong?
How transfer files to global array $_FILES.
Source (upload.php):
echo "<pre>";
print_r ($_FILES);
echo "</pre>";
Source (index.html):
<!DOCTYPE html>
<html>
<head>
<title>Drag and Drop</title>
<style>
body {
background: rgba(211,211,100, .5);
font: 20px Arial;
}
.dropzone {
width: 300px;
height: 300px;
border: 2px dashed #aaa;
color: #aaa;
line-height: 280px;
text-align: center;
position: absolute;
left: 50%;
margin-left: -150px;
top: 50%;
margin-top: -150px;
}
.dropzone.dragover {
color: green;
border: 2px dashed #000;
}
</style>
</head>
<body>
<p>Loaded files:</p>
<div id="uploads">
<ul>
</ul>
</div>
<div class="dropzone" id="dropzone">Drop files</div>
<script>
(function() {
var formData = new FormData();
var dropzone = document.getElementById("dropzone");
dropzone.ondrop = function(e) {
this.className = 'dropzone';
this.innerHTML = 'Drop files';
e.preventDefault();
upload(e.dataTransfer.items);
};
function traverseFileTree(item, path) {
path = path || "";
if (item.isFile) {
item.file(function(file) {
console.log(file); // show info
formData.append('file[]', file); // file exist, but don't append
});
} /*else if (item.isDirectory) {
var dirReader = item.createReader();
dirReader.readEntries(function(entries) {
for (var i=0; i<entries.length; i++) {
traverseFileTree(entries[i], path + item.name + "/");
}
});
}*/
}
var upload = function(items) {
var xhr = new XMLHttpRequest();
for(var i = 0; i < items.length; i++) {
var item = items[i].webkitGetAsEntry();
if (item) {
traverseFileTree(item,'');
}
}
xhr.onload = function() {
console.log(this.responseText);
};
xhr.open('post', 'upload.php');
xhr.send(formData);
};
dropzone.ondragover = function() {
this.className = 'dropzone dragover';
this.innerHTML = 'Mouse up';
return false;
};
dropzone.ondragleave = function() {
this.className = 'dropzone';
this.innerHTML = 'Drop files';
return false;
};
})();
</script>
Both file() and readEntries() return results asynchronously. Since it is impossible to know for certain how many files or directories, which themselves could contain additional directories containing still more files or folders, will be selected and dropped by user, the a single or recursive calls to traverseFileTree require some mechanism to determine when all asynchronous operations have completed. This can be achieved using one or more of several approaches.
The present approach increments a variable n, pushes each individual file to an array uploads. If n is 0, process the first file; increment n so that its value 1 greater than the array containing files .length until the array .length is equal to n - 1
uploads.length === n - 1 || n === 0
then copy uploads array using .slice(), set uploads.length and n to 0, pass array of files to function processFiles where files are appended to FormData() object, call to XMLHttpRequest() is made.
<!DOCTYPE html>
<html>
<head>
<title>Drag and Drop</title>
<style>
body {
background: rgba(211, 211, 100, .5);
font: 20px Arial;
}
.dropzone {
width: 300px;
height: 300px;
border: 2px dashed #aaa;
color: #aaa;
line-height: 280px;
text-align: center;
position: absolute;
left: 50%;
margin-left: -150px;
top: 50%;
margin-top: -150px;
}
.dropzone.dragover {
color: green;
border: 2px dashed #000;
}
</style>
</head>
<body>
<p>Loaded files:</p>
<div id="uploads">
<ul>
</ul>
</div>
<div class="dropzone" id="dropzone">Drop files</div>
<script>
(function() {
var n = 0, uploads = [];
var dropzone = document.getElementById("dropzone");
dropzone.ondrop = function(e) {
this.className = 'dropzone';
this.innerHTML = 'Drop files';
e.preventDefault();
upload(e.dataTransfer.items);
};
function processFiles(files) {
console.log("files:", files);
alert("processing " + files.length + " files");
var formData = new FormData();
// append files to `formData`
for (file of files) {
formData.append("file[]", file, file.name)
}
// check `formData` entries
var curr = 0;
for (data of formData.entries()) {
console.log("formData entry " + curr, data);
++curr;
}
delete curr;
// do ajax stuff here
var xhr = new XMLHttpRequest();
xhr.onload = function() {
console.log(this.responseText);
};
xhr.open("POST", "upload.php");
xhr.send(formData);
}
function traverseFileTree(item, path) {
var handleFiles = function handleFiles(item, path) {
path = path || "";
if (item.isFile) {
item.file(function(file) {
uploads.push(file);
console.log(file, n, uploads.length); // show info
if (uploads.length === n - 1 || n === 0) {
alert("traverseFiles complete, uploads length: "
+ uploads.length);
var files = uploads.slice(0);
n = uploads.length = 0;
processFiles(files)
}
});
} else if (item.isDirectory) {
var dirReader = item.createReader();
dirReader.readEntries(function(entries) {
// increment `n` here
n += entries.length;
for (var i = 0; i < entries.length; i++) {
handleFiles(entries[i], path + item.name + "/");
}
});
}
}
handleFiles(item, path);
}
var upload = function(items) {
if (n !== 0 && uploads.length !== 0) {
n = uploads.length = 0;
}
for (var i = 0; i < items.length; i++) {
var item = items[i].webkitGetAsEntry();
if (item) {
traverseFileTree(item, "");
}
}
};
dropzone.ondragover = function() {
this.className = 'dropzone dragover';
this.innerHTML = 'Mouse up';
return false;
};
dropzone.ondragleave = function() {
this.className = 'dropzone';
this.innerHTML = 'Drop files';
return false;
};
})();
</script>
</body>
</html>
plnkr http://plnkr.co/edit/OdFrwYH2gmbtvHfq3ZjH?p=preview
See also How can I filter out directories from upload handler in Firefox? , How to read files from folder
I am using a little Javascript navigation bar for my single-page site. All of my text links work just fine, but the outbound social media links on the right side are not responding (unless you secondary-click and open it from there). Now, I am just barely knowledgable in JQuery and Javascript...I can understand it and how it works but when it comes to errors I can't figure it out. Thank you for helping! :)
Here is my CSS:
.single-page-nav {
background: rgb(255, 255, 255);
background: rgba(255, 255, 255, .9);
padding: 1.25em 0;
box-shadow: 0px 2px 8px #555;
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index:100000;
}
.single-page-nav ul {
list-style: none;
padding: 0;
margin: 0 auto;
margin-left: -30px;
width: 80%;
overflow: hidden;
}
.single-page-nav li {
float: left;
width: 16%;
text-align: center;
}
.single-page-nav a {
display: block;
color: #000;
font-family: 'Calibri', Helvetica, Sans-Serif;
text-decoration: none;
font-size: 18px;
font-weight: bold;
line-height:1.5em;
}
.single-page-nav a:hover,
.single-page-nav .current {
color: #F92F2C;
}
Here is my HTML
<nav id="menu" role="navigation">
<div class="single-page-nav">
<ul>
<li>Page Top</li>
<li>Watch the Video</li>
<li>Kickstarter</li>
<li>About the Project</li>
<li>Meet the Team</li>
<li>What Are We Doing?</li>
</ul>
<span id="socialtop1">
<img src="/wp-content/images/emailg.png" alt="Email" />
</span>
<span id="socialtop2">
<img src="/wp-content/images/ytg.png" alt="YouTube" />
</span>
<span id="socialtop3">
<img src="/wp-content/images/vmg.png" alt="Vimeo" />
</span>
<span id="socialtop4">
<img src="/wp-content/images/instag.png" alt="Instagram" />
</span>
<span id="socialtop5">
<img src="/wp-content/images/twg.png" alt="Twitter" />
</span>
<span id="socialtop6">
<img src="/wp-content/images/fbg.png" alt="Facebook" />
</span>
</div>
</nav>
And, last but not least, here is the JQuery/Javascript. I didn't write most of it, it's from a tutorial I used.
// Utility
if (typeof Object.create !== 'function') {
Object.create = function(obj) {
function F() {}
F.prototype = obj;
return new F();
};
}
(function($, window, document, undefined) {
"use strict";
var SinglePageNav = {
init: function(options, container) {
this.options = $.extend({}, $.fn.singlePageNav.defaults, options);
this.container = container;
this.$container = $(container);
this.$links = this.$container.find('a');
if (this.options.filter !== '') {
this.$links = this.$links.filter(this.options.filter);
}
this.$window = $(window);
this.$htmlbody = $('html, body');
this.$links.on('click.singlePageNav', $.proxy(this.handleClick, this));
this.didScroll = false;
this.checkPosition();
this.setTimer();
},
handleClick: function(e) {
var self = this,
link = e.currentTarget,
$elem = $(link.hash);
e.preventDefault();
if ($elem.length) { // Make sure the target elem exists
// Prevent active link from cycling during the scroll
self.clearTimer();
// Before scrolling starts
if (typeof self.options.beforeStart === 'function') {
self.options.beforeStart();
}
self.setActiveLink(link.hash);
self.scrollTo($elem, function() {
if (self.options.updateHash) {
document.location.hash = link.hash;
}
self.setTimer();
// After scrolling ends
if (typeof self.options.onComplete === 'function') {
self.options.onComplete();
}
});
}
},
scrollTo: function($elem, callback) {
var self = this;
var target = self.getCoords($elem).top;
var called = false;
self.$htmlbody.stop().animate(
{scrollTop: target},
{
duration: self.options.speed,
complete: function() {
if (typeof callback === 'function' && !called) {
callback();
}
called = true;
}
}
);
},
setTimer: function() {
var self = this;
self.$window.on('scroll.singlePageNav', function() {
self.didScroll = true;
});
self.timer = setInterval(function() {
if (self.didScroll) {
self.didScroll = false;
self.checkPosition();
}
}, 250);
},
clearTimer: function() {
clearInterval(this.timer);
this.$window.off('scroll.singlePageNav');
this.didScroll = false;
},
// Check the scroll position and set the active section
checkPosition: function() {
var scrollPos = this.$window.scrollTop();
var currentSection = this.getCurrentSection(scrollPos);
this.setActiveLink(currentSection);
},
getCoords: function($elem) {
return {
top: Math.round($elem.offset().top) - this.options.offset
};
},
setActiveLink: function(href) {
var $activeLink = this.$container.find("a[href='" + href + "']");
if (!$activeLink.hasClass(this.options.currentClass)) {
this.$links.removeClass(this.options.currentClass);
$activeLink.addClass(this.options.currentClass);
}
},
getCurrentSection: function(scrollPos) {
var i, hash, coords, section;
for (i = 0; i < this.$links.length; i++) {
hash = this.$links[i].hash;
if ($(hash).length) {
coords = this.getCoords($(hash));
if (scrollPos >= coords.top - this.options.threshold) {
section = hash;
}
}
}
// The current section or the first link
return section || this.$links[0].hash;
}
};
$.fn.singlePageNav = function(options) {
return this.each(function() {
var singlePageNav = Object.create(SinglePageNav);
singlePageNav.init(options, this);
});
};
$.fn.singlePageNav.defaults = {
offset: 0,
threshold: 120,
speed: 400,
currentClass: 'current',
updateHash: false,
filter: '',
onComplete: false,
beforeStart: false
};
})(jQuery, window, document);
The problem is that your javascript drastically changes the default link behavior inside of the single-page-nav container. It is applying this javascript functionality to your external links as well, which you do NOT want to have in order for them to work properly.
This bit e.preventDefault(); in your javascript prevents the browser from handling the click event normally.
To fix your problem I would apply the class single-page-nav to the ul (since your social links are outside of this ul) and tweak your CSS a bit.
Personally, I would change the CSS something like so:
.topnav { ... }
.single-page-nav { ... }
.single-page-nav li { ... }
.topnav a { ... }
.topnav a:hover, .topnav .current { ... }