Tech Note: The LGF Pages Bookmarklet Source Code

A deep dive into a Javascript pool
LGF • Views: 39,074
Image via Shutterstock

It’s been a while since I posted the source code for our LGF Pages bookmarklet, and it’s actually undergone some major changes recently, so let’s remedy that omission right now, shall we?

The biggest change: all of the code has been rewritten to use native Javascript, doing away with the need to load the jQuery library. This means the bookmarklet will open more quickly, since the new version only needs to load one external script instead of two.

I’ll start by posting the entire script, then I’ll take it apart section by section to explain how it all works.

/*
LGF Pages Posting Bookmarklet
By Charles F. Johnson
Copyright 2016 Little Green Footballs
All Rights Reserved.

File: lgf-postpage-loader.js
Last revision: 1/1/16 at 10:28 AM
*/
(function() {
	var url = location.href,
		d = document,
		ww = 720,
		wh = 250,
		version = '1.7.0',
		input,
		key,
		value,
		obj,
		theScript,
		noFunctions = RegExp([
			'(\.(?:pdf|jpe?g|gif|png|mp[34])(?:[\?#]|$))',
			'^https?://twitter\.com'
		].join('|'), 'i'),
		popup = window.open(
			'',
			'',
			'scrollbars=1,resizable=1,width=' + ww + ',height=' + wh
		);

	popup.document.write(
		'<!DOCTYPE html>' +
		'<html>' +
		'<head>' +
		'<meta charset="UTF-8">' +
		'<title>LGF Pages</title>' +
		'<meta name="viewport" content="width=device-width, initial-scale=1">' +
		'<style type="text/css">' +
		'*{' +
			'font-family:"Trebuchet MS",sans-serif;' +
			'font-weight:normal;' +
			'margin:0;' +
			'padding:0;' +
			'position:relative;' +
		'}' +
		'window{' +
			'width:' + ww + 'px;' +
			'height:' + wh + 'px;' +
		'}' +
		'body{' +
			'background:#D8E9D8;' +
			'background:linear-gradient(top, #D8E9D8, white) fixed;' +
		'}' +
		'img{' +
			'top:2px;' +
			'width:32px;' +
			'height:32px;' +
		'}' +
		'div{' +
			'width:560px;' +
			'background-color:white;' +
			'border:2px solid #B0B0B0;' +
			'border-radius:12px;' +
			'text-align:center;' +
			'margin:0 auto;' +
			'z-index:1;' +
			'top:-6px;' +
		'}' +
		'h1{' +
			'font-size:42px;' +
			'line-height:42px;' +
			'color:#548563;' +
			'letter-spacing:-0.09em;' +
			'text-align:center;' +
			'z-index:2;' +
		'}' +
		'h3{' +
			'font-size:220%;' +
			'margin:1em 0;' +
			'color:#548563;' +
		'}' +
		'</style>' +
		'</head>' +
		'<body>' +
		'<h1>lgf pages</h1>' +
		'<div>' +
		'<h3><img src="http://littlegreenfootballs.com/bigroller.gif"> Loading...</h3>' +
		'<form action="http://littlegreenfootballs.com/weblog/lgf-postpage.php" method="POST" accept-charset="UTF-8">' +
		'<input type="hidden" name="v" value="' + version + '">' +
		'</form>' +
		'</div>' +
		'</body>' +
		'</html>'
	);
	if (url.match(noFunctions)) {
		cantInject();
	}
	theScript = d.createElement('script');
	theScript.src = '//littlegreenfootballs.com/lgfjs/lgf-postpage-functions.min.js?' + Math.floor(new Date().getTime() / 1000);
	theScript.onload = function() {
		theScript.parentNode.removeChild(theScript);
		doForm($LGF_Page.getQuery(version));
	};
	theScript.onerror = function() {
		cantInject();
	};
	d.getElementsByTagName('head')[0].appendChild(theScript);
	function doForm(obj) {
		var popForm = popup.document.forms[0];
		for (key in obj) {
			input = popup.document.createElement('input');
			input.type = 'hidden';
			input.name = key;
			input.value = obj[key];
			popForm.appendChild(input);
		}
		popForm.submit();
	}
	function cantInject() {
		obj = {
			u: url,
			x: 1
		};
		if (typeof d.title != 'undefined') {
			obj.t = d.title;
			obj.s = d.selection ? d.selection.createRange().text : d.getSelection();
		}
		doForm(obj);
	}
})();

OK. Now that I’ve inflicted all that Javascript upon you, dear reader, let’s dig in and explain what’s going on.

Starting at the top, this entire bookmarklet is wrapped in something known as a self-executing anonymous function (SEAF), so that all the functions and variables contained within it remain private and are only available in the scope of the self-executing function. This is a very important concept for bookmarklets; it ensures that variables can’t conflict with any global variables on the web page in which you invoke the bookmarklet. Because that would be a BAD THING.

It’s “self-executing” because it immediately executes as soon as it’s defined, and “anonymous” because it has no function name. A SEAF typically looks like this:

(function(){
 // the code
})();

And if you look at the beginning and ending lines of our script, you’ll see that all of the code is contained within a SEAF.

The first order of business is defining variables that will be needed within the script; I like to do this at the beginning of a block of code so all the variable definitions are in one place. That’s what lines 11 through 29 do.

But there are a couple of tricky things going on in there. For example:

noFunctions = RegExp([
	'(\.(?:pdf|jpe?g|gif|png|mp[34])(?:[\?#]|$))',
	'^https?://twitter\.com'
].join('|'), 'i')

The “noFunctions” variable is actually a regular expression that checks the URL of the page in which the bookmarklet is invoked, to see if it needs special attention. PDFs, images, and videos can open in a browser window, but they aren’t actual web pages with a Document Object Model, so we need to handle them differently.

This regular expression also checks to see if the current page is on twitter​.com, because Twitter uses special HTTP headers that prevent bookmarklets from injecting script tags — a feature of the LGF Pages bookmarklet we’re about to describe.

I wrote this as an array of strings that are joined together with the regular expression OR operator, so that I can easily add any websites to the list that need the same kind of special treatment as twitter​.com.

The “popup” variable is important to the rest of the code; this variable declaration opens the LGF Pages popup window and returns a reference to the new window.

popup = window.open(
	'',
	'',
	'scrollbars=1,resizable=1,width=' + ww + ',height=' + wh
)

That brings us to lines 31 through 94. This section uses Javascript’s document.write() function to create a web page inside our popup window, that serves as a placeholder while the rest of the script executes, and also contains an HTML form that will be used to send the rest of the page data to the LGF Pages posting script. I broke this section up into a series of strings to make the structure more obvious (and easier to modify as needed).

Lines 95 through 97 use that regular expression we defined above to check the current web page’s URL. If it matches one of the extensions or full URLs in the regex, we jump to the cantInject() function (which I’ll describe soon).

if (url.match(noFunctions)) {
	cantInject();
}

If it’s OK to inject a script tag into the current page’s DOM, we then proceed to do exactly that. Lines 98 through 107 create a script element and set its src attribute to point to the external script containing all the special functions we use to examine a web page and discover any embedded video, audio and PDF files that will be automatically included in the LGF Page posting window.

theScript = d.createElement('script');
theScript.src = '//littlegreenfootballs.com/lgfjs/lgf-postpage-functions.min.js?' + (new Date().getTime());
theScript.onload = function() {
	theScript.parentNode.removeChild(theScript);
	doForm($LGF_Page.getQuery(version));
};
theScript.onerror = function() {
	cantInject();
};
d.getElementsByTagName('head')[0].appendChild(theScript);

We want to prevent the user’s browser from caching the script so that it’s loaded every time the bookmarklet opens, so we append the current time in milliseconds to the script’s address as a query string (following a question mark). This lets us modify the external functions as needed, and know they’ll always be reloaded the next time the bookmarklet is invoked.

We use the script element’s onload callback to invoke the external script’s code when it’s finished loading and initializing; the $LGF_Page.getQuery() function returns an object which we pass to the doForm() function.

If there’s some kind of error that causes the load to fail, the onerror callback jumps to the cantInject() function.

Now we get to the real meat of the enchilada, the function that actually sets up the form in our popup window, fills in the various fields with data, and submits it to the LGF Pages script: doForm(). This function is passed an object, and uses a for/in loop to create hidden inputs that use the object keys as the names of the input elements and the object values as the input elements’ values, appends each new input element to the form, then submits the form. This causes the popup window to come up with the LGF Pages posting form you know and love, with all the fields filled in.

function doForm(obj) {
	var popForm = popup.document.forms[0];
	for (key in obj) {
		input = popup.document.createElement('input');
		input.type = 'hidden';
		input.name = key;
		input.value = obj[key];
		popForm.appendChild(input);
	}
	popForm.submit();
}

Previous versions of the LGF Pages bookmarklet sent all this data to the posting script by using a URL and query string (known as the HTML GET method), but this has severe limitations on length. This new version uses the POST method by submitting a form, which gets rid of the length limits.

Lines 119 through 129 contain the cantInject() function mentioned above. This is the function that’s invoked when we determine that the page we’re on prevents us from injecting script tags, or if there’s an error when we try to load the external function script. This function creates an object to pass to doForm(), containing the URL of the page we’re on and the title and any selected text (if they exist).

function cantInject() {
	obj = {
		u: url,
		x: 1
	};
	if (typeof d.title != 'undefined') {
		obj.t = d.title;
		obj.s = d.selection ? d.selection.createRange().text : d.getSelection();
	}
	doForm(obj);
}

OK. We made it all the way to the end of the lgf-postpage-loader.js bookmarklet. Give yourself a hand if you followed along this far.

But there’s another part to this software entity, the external script called lgf-postpage-functions.js. Just so we have a sense of closure, I’ll post that script now; but I’m not going to do a complete walk-through because I can’t even. I’ll just tell you that this script basically consists of one function defined as part of a global object named $LGF_Page, whose purpose is to find embedded video, audio and other files that would be nice to include in an LGF Page, and also find the underlying HTML code for any selected text. I’ll leave it as an exercise for the coding-inclined to figure out exactly how it does that.

This is loaded as an external file so that it can be updated and modified as needed, when new types of files are identified and new websites added to the list, without making you, dear user, reinstall the bookmarklet for every change.

So without further ado — the rest of the story.

/*
LGF Pages Bookmarklet Functions
Copyright 2016 Little Green Footballs
All Rights Reserved.

File: lgf-postpage-functions.js
Last revision: 1/1/16 at 5:13 PM
*/
var $LGF_Page = {
	version: '1.4.0',
	getQuery: function(loader) {
		if (typeof loader == 'undefined' || parseFloat(loader) < 1.5) {
			alert('This version of the LGF Pages bookmarklet is out of date! Please reinstall the bookmarklet to get the latest version.');
			return;
		}
		var getSelectionHtml = function() {
				var html = '';
				if (typeof window.getSelection != 'undefined') {
					var sel = window.getSelection();
					if (sel.rangeCount) {
						var container = document.createElement('div');
						for (var i = 0, len = sel.rangeCount; i < len; ++i) {
							container.appendChild(sel.getRangeAt(i).cloneContents());
						}
						html = container.innerHTML;
					}
				} else if (typeof document.selection != 'undefined') {
					if (document.selection.type == 'Text') {
						html = document.selection.createRange().htmlText;
					}
				}
				return html;
			},
			ob,
			video = image = audio = iframe = '',
			url = location.href,
			d = document,
			description = getSelectionHtml();
		if (url.match(/www\.npr\.org/i)) {
			ob = d.getElementsByClassName('download');
			if (ob.length) {
				audio = ob[0].getAttribute('href').split('?')[0];
			}
		} else if (url.match(/^http:\/\/www\.msnbc\.com\/[^\/]*\/watch\//i)) {
			ob = d.querySelectorAll('meta[itemprop="embedURL"]');
			if (ob.length) {
				iframe = ob[0].getAttribute('content');
			}
		} else if (url.match(/^http:\/\/www\.msnbc\.com\//i)) {
			ob = d.querySelectorAll('meta[property="nv:videoId"]');
			if (ob.length) {
				iframe = 'http://player.theplatform.com/p/7wvmTC/MSNBCEmbeddedOffSite?guid=' + ob[0].getAttribute('content');
			}
		} else if (url.match(/^http:\/\/www\.nbcnews\.com\//i)) {
			ob = d.getElementById('embed');
			if (ob) {
				var thediv = d.createElement('div');
				thediv.innerHTML = ob.value;
				iframe = thediv.firstChild.getAttribute('src');
			} else {
				ob = d.querySelectorAll('meta[itemprop="embedUrl"]');
				if (ob.length) {
					iframe = ob[0].getAttribute('content');
				}
			}
		} else if (url.match(/video\.pbs\.org\/video\//i)) {
			iframe = 'http://video.pbs.org/viralplayer/' + url.split('/')[4];
			ob = d.querySelectorAll('meta[property="og:image"]');
			if (ob.length) {
				image = ob[0].getAttribute('content');
			}
		} else if (url.match(/www\.rightwingwatch\.org/i)) {
			ob = d.getElementById('content-area').getElementsByTagName('embed');
			if (ob.length) {
				if (ob[0].getAttribute('src').match(/www\.youtube\.com\/v\//i)) {
					iframe = 'https://www.youtube.com/embed/' + ob[0].getAttribute('src').split('?')[0].split('/')[4];
				}
			} else {
				ob = d.getElementById('content-area').getElementsByTagName('iframe');
				if (ob.length) {
					iframe = ob[0].getAttribute('src');
					if (iframe.substring(0, 2) == '//') {
						iframe = 'http:' + iframe;
					}
				}
			}
		}
		if (iframe == '') {
			var embeds = RegExp([
				'//www\.youtube\.com/embed',
				'player\.vimeo\.',
				'w\.soundcloud\.com/player',
				'www\.scribd\.com/embeds/',
				'media\.mtvnservices\.com/embed/',
				'mediamatters\.org/embed/static/clips/'
			].join('|'), 'i');
			ob = [].slice.call(d.querySelectorAll('iframe'));
			if (ob.length) {
				ob = ob.filter(function(e) {
					return ((e.src.match(embeds) || (e.hasAttribute('data-src') && e.getAttribute('data-src').match(/\/\/www\.youtube\.com\/embed/i))) && !e.classList.contains('gaTrackYouTube'));
				});
				if (ob.length && !url.match(/www\.youtube\.com\/|vimeo\.com\/|soundcloud\.com|www\.scribd\.com/i)) {
					ob = ob[0];
					if (ob.hasAttribute('data-src')) {
						iframe = ob.getAttribute('data-src');
					} else {
						iframe = ob.getAttribute('src');
					}
					if (iframe.substring(0, 2) == '//') {
						iframe = 'http:' + iframe;
					}
				}
			}
		}
		return {
			u: url,
			t: d.title,
			f: iframe,
			m: video,
			a: audio,
			i: image,
			s: description
		};
	}
};

Jump to bottom

29 comments

1
thedopefishlives  Jan 3, 2016 • 6:15:53pm

Random question. You said in the note that you switched from using HTTP GET to HTTP POST - is there a reason why you chose GET in the first place, given that form submission is basically why the POST verb was created in the first place?

2
Ace-o-aces  Jan 3, 2016 • 6:16:17pm

Today in false equivalence: From a Bundy defender…

3
Backwoods_Sleuth  Jan 3, 2016 • 6:17:49pm

Do we have to replace our bookmarklet bookmark?

4
Charles Johnson  Jan 3, 2016 • 6:20:03pm

re: #1 thedopefishlives

Random question. You said in the note that you switched from using HTTP GET to HTTP POST - is there a reason why you chose GET in the first place, given that form submission is basically why the POST verb was created in the first place?

Because it was easier.

5
thedopefishlives  Jan 3, 2016 • 6:20:57pm

re: #4 Charles Johnson

Because it was easier.

Spoken like a true programmer.

6
Targetpractice  Jan 3, 2016 • 6:21:05pm

Thank you, Captain Obvious.

7
Belafon  Jan 3, 2016 • 6:22:39pm

re: #6 Targetpractice

[Embedded content]

Thank you, Captain Obvious.

Yes, but sometimes the guy with a uniform has to say it.

8
Ace-o-aces  Jan 3, 2016 • 6:25:56pm

OK, you can stop internetting for today, this guy has won it:

9
The Vicious Babushka  Jan 3, 2016 • 6:26:47pm

STUPIDEST MEME OF THE DAY==>
This illiterate probably calls college-educated people, “Libtards”

10
Camacho DeezNuts 2016  Jan 3, 2016 • 6:27:47pm

re: #8 Ace-o-aces

Hm… that’s good. How about this:

11
Targetpractice  Jan 3, 2016 • 6:28:51pm

re: #9 The Vicious Babushka

STUPIDEST MEME OF THE DAY==>
This illiterate probably calls college-educated people, “Libtards”

[Embedded content]

I doubt any of those camped out there own a single parcel of land in Oregon. The guys who do have chosen to quietly surrender themselves to authorities and serve out the remainder of their sentences for arson which put the lives of firefighters in jeopardy.

12
Great White Snark  Jan 3, 2016 • 6:29:35pm

This is the first version that updates itself right? Need to remove the old ones from my various computers?

13
Bill and Opus for 2016!  Jan 3, 2016 • 6:29:54pm

I notice that .webm (compact imbedded video) files aren’t included in your noFunctions definitions. Do they just not require special handling in the fashion that other graphic/sound media types do?

14
Backwoods_Sleuth  Jan 3, 2016 • 6:30:36pm
15
thedopefishlives  Jan 3, 2016 • 6:30:39pm

re: #13 Bill and Opus for 2016!

I notice that .webm (compact imbedded video) files aren’t included in your noFunctions definitions. Do they just not require special handling in the fashion that other graphic/sound media types do?

Probably because .webm files can’t be displayed as standalone documents.

16
Cheechako  Jan 3, 2016 • 6:34:44pm

Once the Nevada BLMers get back from their holidays (use or loose vacation time) they might want to head out to the Bundy Ranch. Looks like nobody is home. Time to round up the trespassing cattle.

17
Belafon  Jan 3, 2016 • 6:37:42pm

re: #16 Cheechako

Dad’s still at home.

18
wheat-dogghazi-mailgate  Jan 3, 2016 • 6:38:26pm

Charles, since we’re discussing tech issues, how do you update the LGF Facebook page? Do you do it manually, or is there some spiffy FB API that updates whenever there’s a new post here?

I use a WordPress plugin for my site, which automatically posts to my FB page, but periodically I have to reauthorize it, because FB “forgets” it.

19
William Lewis  Jan 3, 2016 • 6:38:43pm

re: #17 Belafon

Dad’s still at home.

Good opportunity to put him in a cell for a nice long time.

20
The Vicious Babushka  Jan 3, 2016 • 6:40:48pm

U MAD BRO

21
Targetpractice  Jan 3, 2016 • 6:41:17pm

re: #19 William Lewis

Good opportunity to put him in a cell for a nice long time.

Yeah, what are the boys and their schoolyard buddies going to do, hop the next flight back home while BLM is executing the court order? Might be a good idea, before doing so, to announce that anybody who leaves the refuge will immediately be arrested.

22
Targetpractice  Jan 3, 2016 • 6:42:41pm

re: #20 The Vicious Babushka

U MAD BRO

[Embedded content]

Got the tinfoil beanie on a little tight, don’t he?

23
Camacho DeezNuts 2016  Jan 3, 2016 • 6:49:48pm

re: #20 The Vicious Babushka

Let go of the hate in your heart you no good piece of shit. LOL

24
goddamnedfrank  Jan 3, 2016 • 6:58:09pm
25
The Vicious Babushka  Jan 3, 2016 • 6:58:45pm

re: #24 goddamnedfrank

[Embedded content]

He seems nice.

26
Camacho DeezNuts 2016  Jan 3, 2016 • 7:01:24pm

re: #25 The Vicious Babushka

He seems nice.

No hate in his heart at all. None. Not a smidge of hate. Nothing.

27
Charles Johnson  Jan 3, 2016 • 7:27:20pm

re: #18 wheat-dogghazi-mailgate

Charles, since we’re discussing tech issues, how do you update the LGF Facebook page? Do you do it manually, or is there some spiffy FB API that updates whenever there’s a new post here?

I use a WordPress plugin for my site, which automatically posts to my FB page, but periodically I have to reauthorize it, because FB “forgets” it.

The posting script automatically updates the LGF Facebook page. The authorization token from Facebook expires every 60 days or so, so my script actually renews the authorization token when that happens.

This was a major pain to get working, but now it’s all automatic.

28
wheat-dogghazi-mailgate  Jan 3, 2016 • 7:44:42pm

re: #27 Charles Johnson

The posting script automatically updates the LGF Facebook page. The authorization token from Facebook expires every 60 days or so, so my script actually renews the authorization token when that happens.

This was a major pain to get working, but now it’s all automatic.

I am in awe.

29
Eventual Carrion  Jan 3, 2016 • 8:47:45pm

re: #6 Targetpractice

[Embedded content]

Thank you, Captain Obvious.

They learned well from the Benghazi terrorists. Blend in with an ongoing protest and launch your armed terrorism under their cover.


This article has been archived.
Comments are closed.

Jump to top

Create a PageThis is the LGF Pages posting bookmarklet. To use it, drag this button to your browser's bookmark bar, and title it 'LGF Pages' (or whatever you like). Then browse to a site you want to post, select some text on the page to use for a quote, click the bookmarklet, and the Pages posting window will appear with the title, text, and any embedded video or audio files already filled in, ready to go.
Or... you can just click this button to open the Pages posting window right away.
Last updated: 2016-01-01 10:29 am PST
LGF User's Guide RSS Feeds Tweet

Help support Little Green Footballs!

Subscribe now for ad-free access!Register and sign in to a free LGF account before subscribing, and your ad-free access will be automatically enabled.

Recent PagesClick to refresh
Mike Pence and the Delicate Art of Protest.blog.timesunion.com
rhoffman
13 hours, 22 minutes ago
Views: 329 • Comments: 2 • Rating: -2
Tweets: 1 • Share to Facebook
Shares: 1
Comments: 0
: 1
READ THE WHOLE THING: Sexual Violence Is a Hate Crime - National Organization for Women I strongly believe violence against women, and particularly sexual violence, is a hate crime – and that may surprise people, even feminists. On October 2, 2006 a man walked into an Amish schoolhouse in Lancaster, County Pennsylvania and shot ...
Birth Control Works
13 hours, 30 minutes ago
Views: 271 • Comments: 0 • Rating: 1
Tweets: 0 • Share to Facebook
Shares: 0
Comments: 0
: 0
What I Learned After My Study on Men Secretly Removing Condoms Went Viral But it struck a chord, and somehow managed to trigger an international conversation about assholes who remove condoms during sex without their partners' permission. (Some people call this "stealthing," but I think the term trivializes the harm.) The paper ...
Birth Control Works
1 day ago
Views: 319 • Comments: 0 • Rating: 0
Tweets: 0 • Share to Facebook
Shares: 0
Comments: 0
: 0
‘Racist’ Flyers Urge Black Chicagoans to Help Deport Latino Neighbors - Pilsen - DNAinfo Chicago CHICAGO — A South Side alderman is slamming a flyer found in neighborhoods around the city asking black Chicagoans to report suspected undocumented residents to immigration agents. The flyer says that "Sanctuary city Policies endanger the lively hood[sic] of ...
Birth Control Works
1 day ago
Views: 253 • Comments: 0 • Rating: 0
Tweets: 0 • Share to Facebook
Shares: 0
Comments: 0
: 0
You Have One Last Chance to Repent, Sean Hannity….Help me force Sean Hannity to repent. If he doesn't, The Major will bring incredible the damage to Mr. Hannity using the new weapons of The Internet. I tried to warn you, Sean. You didn't listen then. If you choose ...
The Major
1 day, 21 hours ago
Views: 340 • Comments: 0 • Rating: 0
Tweets: 2 • Share to Facebook
Shares: 0
Comments: 0
: 0
Doctors Argue That Female Genital Mutilation Is Protected Under First Amendment Two doctors in Detroit, along with one of their wives, are about to take the first religious defense of female genital mutilation to a US Federal court. The case stems from a FBI investigation into Dr. Jumana Nagarwala after ...
Birth Control Works
1 day, 23 hours ago
Views: 442 • Comments: 0 • Rating: 2
Tweets: 3 • Share to Facebook
Shares: 1
Comments: 0
: 1
Letters From Women Pleading for Abortion, Sent in 1917, Mirror Emails Sent Today In the early 1900s, desperate American women wrote letters to the founder of Planned Parenthood begging for help with unwanted pregnancies. A century later, they're sending eerily similar messages to an international abortion-by-mail service. "I'm in the family way ...
Birth Control Works
2 days, 1 hour ago
Views: 404 • Comments: 0 • Rating: 1
Tweets: 0 • Share to Facebook
Shares: 1
Comments: 0
: 1
Pregnant Women Are Being Arrested for Crimes They Didn’t Know They Committed Laurie was five months pregnant when she used methamphetamine. She'd previously been on anxiety medication, but her doctor switched her to an opiate when she told him she had history for drug addiction. She said the opiate made her ...
Birth Control Works
2 days, 1 hour ago
Views: 5,684 • Comments: 0 • Rating: 1
Tweets: 0 • Share to Facebook
Shares: 17
Comments: 1
: 17
Inclusive Approaches to Preventing Violent Extremism - Carter Center Violent extremism fractures communities, fosters division, and exacerbates political conflicts. It knows no religious, national, or ethnic boundaries. Violent extremist movements continue to fuel the world’s most violent wars in Africa and the Middle East, and right-wing ethno-nationalist extremism ...
Birth Control Works
2 days, 5 hours ago
Views: 316 • Comments: 0 • Rating: 0
Tweets: 0 • Share to Facebook
Shares: 1
Comments: 0
: 1
Love It or Hate It-What Saudi Arabia Is Buying in Its $110 Billion Arms Deal Huge deals with israel, and Saudi Arabia. Our industrial war machine grows apace. TanksSaudi Arabia is expecting 153 M1A1/A2 tank structures, which will be turned into 133 Saudi-specification M1A2S main battle tanks. Twenty additional tanks will replace those from ...
Unshaken Defiance
2 days, 6 hours ago
Views: 409 • Comments: 0 • Rating: 0
Tweets: 0 • Share to Facebook
Shares: 1
Comments: 0
: 1