Metro Goldwyn Mayer Wiki

Octopussy is a 1983 British spy film and the thirteenth in the James Bond series produced by Eon Productions; the sixth to star Roger Moore as the fictional MI6 agent James Bond. It was directed by John Glen and the screenplay was written by George MacDonald Fraser, Richard Maibaum and Michael G. Wilson.

The film's title is taken from a short story in Ian Fleming's 1966 short story collection Octopussy and The Living Daylights, although the film's plot is mostly original. It does, however, contain a scene adapted from the Fleming short story "The Property of a Lady" (included in 1967 and later editions of Octopussy and The Living Daylights). The events of the short story "Octopussy" form part of the title character's background and are recounted by her in the film.

In Octopussy, Bond is assigned the task of following a megalomaniacal Soviet general (Steven Berkoff) who is stealing jewellery and art objects from the Kremlin art repository. This leads Bond to a wealthy exiled Afghan prince, Kamal Khan (Louis Jourdan), and his associate, Octopussy (Maud Adams), and the discovery of a plot to force disarmament in Western Europe with the use of a nuclear weapon.

Octopussy was produced by Albert R. Broccoli and Michael G. Wilson; it was released four months before the non-Eon Bond film Never Say Never Again. The film earned $187.5 million against its $27.5 million budget and received mixed reviews. Praise was directed towards the action sequences and locations, with the plot and humour being targeted for criticism; Maud Adams's portrayal of the title character also drew polarised responses.

Plot[]

After fleeing knife-throwing twin assassins Mischka and Grishka in East Berlin, mortally wounded British agent 009, dressed as a circus clown and carrying a counterfeit Fabergé egg, crashes into the British ambassador's residence and dies. MI6 immediately suspects Soviet involvement and, after the genuine Fabergé egg is to be auctioned in London, sends James Bond to identify the seller. At the auction, Bond swaps the fake egg for the real one and subsequently engages in a bidding war with an exiled Afghan prince named Kamal Khan, forcing Khan to pay £500,000 for the counterfeit. Bond follows Khan to his palace in India. Bond defeats Khan in a game of backgammon using Khan's loaded dice. Bond and his MI6 contact, Vijay, escape Khan's bodyguard Gobinda. Later, Khan's associate Magda seduces Bond. He notices her blue-ringed octopus tattoo. Bond permits Magda to steal the real Fabergé egg, which is fitted with Q's listening and tracking device. Gobinda knocks Bond unconscious and takes him to Khan's palace. After Bond escapes, he listens in on the bug and discovers that Khan works with Orlov, a Soviet general seeking to expand Soviet domination to Western Europe.

Bond infiltrates a floating palace in Udaipur and meets its owner, Octopussy, a wealthy businesswoman, smuggler and Khan's associate. She also leads the Octopus cult, of which Magda is a member. Octopussy has a personal connection with Bond: her father is the late Major Dexter-Smythe, whom Bond arrested for treason. Octopussy thanks Bond for allowing the Major to commit suicide rather than face trial. She invites Bond to be her guest. Earlier in Khan's palace and later in Octopussy's palace, Bond discovers that Orlov has been supplying Khan with priceless Soviet treasures, replacing them with replicas while Khan has been smuggling the genuine objects into the West via Octopussy's circus troupe. Orlov is planning to meet Khan at Karl-Marx-Stadt in East Germany, where the circus is scheduled to perform. Khan's mercenaries break into the palace to kill Bond, but Bond and Octopussy thwart them. Bond learns from Q that thugs have killed Vijay.

Travelling to East Germany, Bond infiltrates the circus and discovers that Orlov has replaced the Soviet treasures with a nuclear warhead, primed to explode during the circus performance at a US Air Force base in West Germany. The explosion would force Europe into seeking unilateral disarmament in the belief that the bomb belonged to the US and was detonated at the airbase accidentally, leaving the unprotected borders open to a Soviet invasion. Bond takes Orlov's car, drives it along the railroad tracks and boards the moving circus train. Orlov gives chase, but is killed by border guards after he tries to rush a checkpoint. Bond kills Mischka and Grischka, and after falling from the train, commandeers a car to get to the airbase. Bond penetrates the base and disguises himself as a clown to evade the West German police. He attempts to convince Octopussy that Khan has betrayed her and she realizes that she has been tricked. She assists Bond in deactivating the warhead.

Some time later, with the plan foiled, Khan has returned to his palace and prepares to flee. Bond and Octopussy also return separately to India. Bond arrives at Khan's palace just as Octopussy and her troops launch an assault on the grounds. Octopussy attempts to kill Khan, but is captured by Gobinda. While Octopussy's team, led by Magda, overpower Khan's guards, Khan and Gobinda abandon the palace, taking Octopussy as a hostage. As they attempt to escape in their airplane, Bond clings to the fuselage and disables an engine and the elevator panel. Struggling with Bond, Gobinda plummets off the plane's roof to his death, and Bond and Octopussy jump off the plane onto a nearby cliff only seconds before Khan fatally crashes into a mountain. While the Minister of Defence and General Gogol discuss the transport of the jewelry, Bond recuperates with Octopussy aboard her private yacht in India.

Cast[]

  • Roger Moore as James Bond, MI6 agent '007'.
  • Maud Adams as Octopussy, a jewel smuggler and wealthy businesswoman. Adams previously played a different character in The Man With the Golden Gun.
  • Louis Jourdan as Kamal Khan, an exiled Afghan prince.
  • Kristina Wayborn as Magda, trusted subordinate and henchwoman to Octopussy and Khan.
  • Kabir Bedi as Gobinda, Khan's powerful bodyguard.
  • Steven Berkoff as General Orlov, a renegade Soviet general who works with Khan to bomb a US airbase.
  • Vijay Amritraj as Vijay, Bond's MI6 ally in India. This was Armitaj's acting debut after gaining prominence as a tennis player.
  • David Meyer and Anthony Meyer as Mischka and Grischka (credited as Twin One and Twin Two): Orlov's knife-throwing henchmen who are performers in Octopussy's circus.
  • Douglas Wilmer as Jim Fanning, antiquities expert who accompanies Bond at the Fabergé auction.
  • Robert Brown as M, head of the British Secret Service and Bond's superior.
  • Walter Gotell as General Anatoly Gogol, director of the KGB.
  • Desmond Llewelyn as Q, MI6's gadget designer. Llewelyn was disappointed that he was unable to travel to India since his scenes were filmed at Pinewood Studios.[2]
  • Lois Maxwell as Miss Moneypenny, M's secretary.
  • Geoffrey Keen as British Minister of Defence Frederick Gray (credited as Minister of Defence).
  • Albert Moses as Sadruddin, head of MI6 station in India, assigned to assist Bond.
  • Bruce Boa as U.S. Air Force General Peterson, the American base commander in West Germany.
  • Michaela Clavell as Penelope Smallbone, Moneypenny's assistant.
  • Paul Hardwick as the Soviet Chairman who presides over meeting between Orlov and Gogol. Hardwick was cast due to his resemblance to Soviet premier Leonid Brezhnev, but Brezhnev died during production in November 1982, making his presence an anachronism.[3] This was Hardwick's final film role.

Other actors in smaller roles include Andy Bradford as MI6 agent 009, Dermot Crowley as Lieutenant Kamp, Orlov's nuclear weapons expert; Peter Porteous as Lenkin, the Kremlin art expert; Eva Rueber-Staier as Rublevitch, Gogol's secretary; Jeremy Bulloch as Smithers, Q's assistant; and Richard LeParmentier as General Peterson's aide. Ingrid Pitt has an uncredited voice cameo as Octopussy's galley mistress.

Production[]

Writing[]

Despite financial problems at United Artists after the release of Michael Cimino's Heaven's Gate, the studio greenlit another James Bond film to be produced and released in 1982. In March 1983, one month after the announcement, UA was purchased and merged into Metro-Goldwyn-Mayer.[4] Michael G. Wilson, Richard Maibaum, and George MacDonald Fraser were hired to write a film based on short stories from Ian Fleming's posthumous collection Octopussy and The Living Daylights.[5] Little of the plot of the short story "Octopussy" is used, however, with its events simply related by Bond as the family backstory for one of the main characters. The scene at Sotheby's is, though, adapted from the short story "The Property of a Lady" (included in 1967 and later editions of the collection), while Kamal Khan's reaction following the backgammon game is taken from Fleming's novel Moonraker.[6] After initially intending the film to be set in Japan, Fraser chose India as the setting because of his extensive research on the country for his novel Flashman.[5]

Fraser was hired to work on an early draft of the script and he proposed that the story be set in India, as the series had not yet visited said country.[7] The first draft was delivered shortly after the release of For Your Eyes Only,[6] whose writers Michael G. Wilson and Richard Maibaum went on to rework the script. They discarded his idea for the opening sequence, featuring a motorbike chase set at the Isle of Man TT, but still retained moments that producer Albert R. Broccoli had first criticized, where Bond dressed as a gorilla and later, a clown.[7] The film was rewritten to focus on jewelry smuggling after a scandal in the Soviet Union involving General Secretary Leonid Brezhnev's son Yuri Brezhnev manipulating the Moscow State Circus to smuggle jewelry.[4]

Casting[]

File:Brolin.JPG

James Brolin's screen test as James Bond, with Vijay Amritraj

Following For Your Eyes Only, Roger Moore had expressed a desire to retire from the role of James Bond. His original contract had been for three films, which was fulfilled with The Spy Who Loved Me in 1977. Moore's following two films (Moonraker in 1979 and For Your Eyes Only in 1981) were negotiated on a film-by-film basis. Given his reluctance to return for Octopussy, the producers engaged in a semi-public quest for the next Bond, with Timothy Dalton and Lewis Collins[4] being suggested as a replacement and screen tests carried out with Michael Billington, Oliver Tobias, and American actor James Brolin.[5] However, when rival Bond production Never Say Never Again was announced, the producers persuaded Moore to continue in the role as it was thought the established actor would fare better against former Bond Sean Connery.[8] It has been reported that Brolin had been hired and was actually on the point of moving to London to begin work on Octopussy at the time, while Broccoli refused to dispute Tobias's public statements that he was about to be cast as Bond.[9][5]

Sybil Danning was announced in Prevue magazine in 1982 as being Octopussy, but was never actually cast, later explaining that Albert R. Broccoli felt "her personality was too strong".[10] Faye Dunaway was deemed too expensive. Barbara Carrera said she turned down the role in order to play Fatima Blush in the competing Bond film Never Say Never Again. Casting director Jane Jenkins revealed that the Bond producers told her that they wanted a South Asian actress to play Octopussy, so she looked at the only two Indians in predominantly white Hollywood, Persis Khambatta and Susie Coelho. Afterward, she auditioned white actresses, like Barbara Parkins and Kathleen Turner,[4] who she felt could pass for Indian. Finally, Broccoli announced to her that they would cast Swedish-born Maud Adams, who had been a Bond girl in The Man with the Golden Gun, and had been recently used by Eon to screen test the potential Bonds. To acknowledge the nationality, Adams had her hair darkened, and a few lines were added about how she was raised by an Indian family. A different plotline, with Adams's British father exposed as a traitor, was used instead.[11] The role of Magda went to another Swedish actress, Kristina Wayborn, who the producers first noticed playing Greta Garbo in the miniseries The Silent Lovers.[8]

Octopussy is also the first film to feature Robert Brown as M, following the death of Bernard Lee in 1981. Brown was recommended by Moore, who had known him since both worked in the series Ivanhoe.[12] Brown had previously played Admiral Hargreaves in The Spy Who Loved Me, six years earlier.[13]

The first actor to be cast in the film was Vijay Amritraj, a popular professional tennis player whom Broccoli met watching The Championships in Wimbledon. His character of Bond's ally in India was also named Vijay and used a tennis racket as a weapon. For the villains, Broccoli brought in his friend Louis Jourdan as Kamal Khan, while his daughter Barbara suggested Steven Berkoff for Orlov after having seen him perform his own play, Greek, in Los Angeles.[8]

Filming[]

File:RAF Northolt hangar.JPG

The 311 hangar at RAF Northolt used for filming the jet stunt scene

The filming of Octopussy began in West Berlin on 10 August 1982 with the scene in which Bond arrives at Checkpoint Charlie.[14] Other locations from the city included Spandau Prison, the Brandenburg Gate, and Potsdamer Platz.[5] Principal photography was done by Arthur Wooster and his second unit, who later filmed the knife-throwing scenes.[15] Filming in India began on 12 September 1982 in Udaipur, Rajasthan.[5] The Monsoon Palace served as the exterior of Kamal Khan's palace, while scenes set at Octopussy's palace were filmed at the Lake Palace and Jag Mandir, and Bond's hotel was the Shiv Niwas Palace.[8] In England RAF Northolt, RAF Upper Heyford and RAF Oakley were the main locations.[16] The Karl-Marx-Stadt railways scenes were shot at the Nene Valley Railway in Peterborough, while studio work was performed at Pinewood Studios and the 007 Stage.[17] Parts of the film were also shot in Hurricane Mesa, Hurricane-LaVerkin Bridge, and New Harmony in Utah.[18] Most of the crew as well as Roger Moore had diet problems while shooting in India.[2]

File:Sajjan garh.jpg

The Monsoon Palace

The pre-title sequence has a scene where Bond flies a nimble homebuilt Bede BD-5J aircraft through an open hangar.[15] Hollywood stunt pilot and aerial co-ordinator J.W. "Corkey" Fornof, who piloted the aircraft at more than 150 miles per hour (240 km/h) 150 miles per hour (240 km/h), has said, "Today, few directors would consider such a stunt. They'd just whip it up in a computer lab."[19] Having collapsible wings, the plane was shown hidden in a horse trailer; however, a dummy was used for this shot.[20] Filming inside the hangar was achieved by attaching the aircraft to an old Jaguar car with a steel pole, driving with the roof removed.[15] The second unit were able to add enough obstacles including people and objects inside the hangar to hide the car and the pole and make it look as though Moore was flying inside the base. For the explosion after the mini jet escapes, however, a miniature of the hangar was constructed and filmed up close. The exploding pieces of the hangar were in reality only 4 inches (10 cm)* 4 inches (10 cm)* long.[8]

Much later in the film, Bond steals a Mercedes-Benz saloon car at a depot defended by antagonist soldiers; as he tries to escape, he drives over barrier spikes which shred the vehicle's tyres and then manoeuvres his vehicle's bare wheels onto the rails to pursue Octopussy's circus train. During filming, the car had intact tyres in one scene so as to avoid any mishap.[20]

File:Acrostar.jpg

Acrostar from Octopussy seen at a convention

Stunt coordinator Martin Grace suffered an injury while shooting the scene where Bond climbs down the train to catch Octopussy's attention.[21] During the second day of filming, Grace – who was Roger Moore's stunt double for the scene – carried on doing the scene longer than he should have, due to a miscommunication with the second unit director, and the train entered a section of the track which the team had not properly surveyed. Shortly afterwards, a concrete pole fractured Grace's left leg. The cyclist seen passing in the middle of a sword fight during the baby taxi chase sequence was in fact a bystander who passed through the shot, oblivious to the filming; his intrusion was captured by two cameras and left in the final film.[8] Cameraman Alan Hume's last scene was that of Octopussy's followers rowing. That day, little time was left and it was decided to film the sunset at the eleventh hour.[22]

The Fabergé egg in the film is based on a real one; made in 1897 and which was called the Coronation Egg. The egg in the film is listed in the auction catalogue as being "The Property of a Lady", which is the name of one of Ian Fleming's short stories released in more recent editions of the collection Octopussy and The Living Daylights.

In a bit of diegesis that "breaks the fourth wall", Vijay signals his affiliation to MI6 by playing the "James Bond Theme" on a recorder while Bond is disembarking from a boat in the harbour near the City Palace.[23] Like his fictional counterpart, the real Vijay had a distinct fear of snakes and found it difficult to hold the basket during filming.[8]

Music[]

Main article: Octopussy (soundtrack)

After being absent in For Your Eyes Only due to tax problems, John Barry returned to do his ninth Bond score.[24] Barry made frequent references to the James Bond Theme to reinforce Octopussy as the official Bond film, given that the motif could not be featured in Never Say Never Again, and opted to include only subtle references to the music of India, avoiding instruments such as the sitar for feeling that authentic music "didn't work dramatically". He also wrote opening theme "All Time High" with lyricist Tim Rice. "All Time High", sung by Rita Coolidge, is one of seven musical themes in the James Bond series whose song titles do not refer to the film's title. "All Time High" spent four weeks at number one on the United States' Adult Contemporary singles chart and reached number 36 on the Billboard Hot 100.[23]

The soundtrack album was released in 1985 by A&M Records; the compact disc version of this release was recalled due to a colour printing error which omitted the credits from the album cover, making it a rare collector's item. In 1997, the soundtrack was re-issued by Rykodisc, with the original soundtrack music and some film dialogue, on an Enhanced CD version. The 2003 release, by EMI, restored the original soundtrack music without dialogue.[25]

Release and reception[]

Octopussy was the first Bond film released by Metro-Goldwyn-Mayer, which had absorbed United Artists, the previous distributor of Eon Bond films. Octopussy premiered at the Odeon Leicester Square on 6 June 1983, with Prince Charles and Diana, Princess of Wales in attendance.[26] The film earned slightly less than For Your Eyes Only, but still grossed $187.5 million, with $67.8 million in the United States alone.[27] It also performed better than Never Say Never Again, the non-Eon Bond remake of Thunderball which was released a few months later and gathered $55 million in North America.[28] At the 11th Saturn Awards, Maud Adams was nominated for Best Supporting Actress.[29] The film won the Golden Reel Award for Best Sound Editing.[30] In Germany, it won the Golden Screen Award for selling over 3 million tickets.[31]

Contemporary reviews[]

Gary Arnold of The Washington Post felt Octopussy was "one of the snazziest, wittiest productions" of the film series, in which he praised John Glen's direction, Louis Jourdan's performance, and the screenplay.[32] Writing for The New York Times, Vincent Canby praised the film, but noted how "much of the story is incomprehensible".[33] Gene Siskel, reviewing for The Chicago Tribune, awarded the film three stars out of four, stating the film was "surprisingly entertaining—surprising because in his previous five Bond appearances Roger Moore has always come off as a smug stiff. In Octopussy Moore relaxes a bit and, just as important, his role is subordinated to the film's many and extremely exciting action scenes. Octopussy has the most sustained excitement in a Bond film since You Only Live Twice." However, he felt that the character Octopussy was detrimental to the film and that the action "blunts a script that is weak on characterization and long on male chauvinism".[34]

Variety felt the film's strong points were "the spectacular aerial stuntwork marking both the pre-credits teaser and extremely dangerous-looking climax. The rest of the action scenes are well-executed but suffer from a sense of deja vu, as in a speeding train that recalls Sean Connery's derring-do in The Great Train Robbery".[35] Kevin Thomas of the Los Angeles Times felt the film proved "to be business as usual, no better or worse than most of its predecessors. After all this time, it's amazing that the same old formula still plays: the gadgetry, gorgeous girls, travelogue locales and the shameless double-entendres—in this instance, octo-entendres." He complimented Glen's direction, but further remarked that the screenwriters had "given him too much to unravel. At 2 hours and 10 minutes, Octopussy seems a good 20 to 30 minutes too long for light escapist fare. The familiar chases and old-time serial-type cliff-hanging crises come fast but a mite too thick."[36]

Retrospective reviews[]

James Berardinelli claimed that the movie was long and confusing, and strongly criticised Steven Berkoff's performance, describing it as "offensively bad" and the worst performance of any Bond villain.[37] A particular point of contention are comedic scenes where Bond is dressed in a clown costume, a gorilla outfit and doing a Tarzan yell during a jungle chase.[38] As a result, it frequently ranks low in rankings of James Bond films, such as the ones by Entertainment Weekly,[39] MSN,[40] and IGN.[41] C.J. Henderson reviewed Octopussy in The Space Gamer No. 65.[42] Henderson commented that "there isn't a moment in the movie when we worry for the slightest instant that anything could happen to suave ol' James. Predictably, it doesn't. To kill Bond would be to lose the most bankable genre character ever brought to the movies."[42] On Rotten Tomatoes, the film has an approval rating of 43% based on 49 reviews with an average rating of 5.20/10. The website's critical consensus reads: "Despite a couple of electrifying action sequences, Octopussy is a formulaic, anachronistic Bond outing."[43]

By contrast, the elegance of the film locations in India, and the stunts on the aircraft and train were appreciated.[44] GQ writer David Williams said Octopussy was "one of the best 'Bad Films' of the franchise", praising the entertaining characters but finding the silliness and Moore's advanced age problematic.[45] Danny Peary wrote that Octopussy "has slow spots, little humour, and villains who aren't nearly of the calibre of Dr. No, Goldfinger, or Blofeld. Also, the filmmakers make the mistake of demeaning Bond by having him swing through the trees and emitting a Tarzan cry and having him hide in a gorilla suit and later disguise himself as a clown (who all the kids at the circus laugh at). It's as if they're trying to remind us that everything is tongue-in-cheek, but that makes little sense, for the film is much more serious than typical Bond outings – in fact, it recalls the tone of From Russia with Love."[46]

Character reviews[]

In 2006, Fandango ranked the character Octopussy as one of the top-10 Bond girls, and described her as "a powerful, impressive woman".[47] Entertainment Weekly, however, ranked her as the 10th-worst Bond girl in one list in 2006[48] but as the best "babe" of the Roger Moore James Bond films in another list in 2008.[49] A poll by Bond fans in 2008 elected Octopussy as the tenth-worst Bond Girl.[50] Yahoo! Movies included the character in a 2012 list of the best Bond girl names, commenting: "This Bond girl moniker was so good, they named the film after her!"[51]

See also[]

Lua error: bad argument #2 to 'title.new' (unrecognized namespace name 'Portal').

  • James Bond in film
  • Outline of James Bond
  • The Fourth Protocol

References[]

  1. Octopussy. European Audiovisual Observatory.
  2. 2.0 2.1 Hume, 121
  3. MI6 :: The Home of James Bond (en).
  4. 4.0 4.1 4.2 4.3 Field, Matthew (2015). Some kind of hero : 007 : the remarkable story of the James Bond films. ISBN 978-0-7509-6421-0. 
  5. 5.0 5.1 5.2 5.3 5.4 5.5 Octopussy.
  6. 6.0 6.1 (2001) Kiss Kiss Bang! Bang!: the Unofficial James Bond Film Companion. Batsford Books, page 149. ISBN 978-0-7134-8182-2. 
  7. 7.0 7.1 Fraser, George MacDonald (2019). "Shooting Script 8 - You Want to Put Bond in a Gorilla Suit?", The Light's on at Signpost. HarperCollins, page 234–46. ISBN 978-0008337285. 
  8. 8.0 8.1 8.2 8.3 8.4 8.5 8.6 Template:Cite video
  9. Jacks, Kelso (7 April 2020). Roger Moore Was Almost Replaced As James Bond: Watch James Brolin's Audition.
  10. 11 Questions With Sybil Danning. Battle Royale With Cheese (7 May 2012).
  11. Janet Hirshenson, Jane Jenkins (2007). A Star is Found: Our Adventures Casting Some of Hollywood's Biggest Movies. Houghton Mifflin Harcourt, page 35–7. ISBN 978-0547545264. 
  12. Moore, Roger (2012). Bond on Bond: The Ultimate Book on 50 Years of Bond Movies. Lyons Press, page 278. ISBN 978-1843178859. 
  13. Robert Brown, 82; Actor Played Spy Boss M in 4 Bond Films. Los Angeles Times (21 November 2003).
  14. August: This Month in Bond History.
  15. 15.0 15.1 15.2 Hume, Alan (May 2004). "Potted Palms", A Life Through the Lens: Memoirs of a Film Cameraman. McFarland & Company, page 122. ISBN 0-7864-1803-6. 
  16. 19 more top secret Bond locations around Britain. The Telegraph (29 October 2015).
  17. 19 top secret Bond locations around Britain. The Telegraph (28 October 2015).
  18. (2010) When Hollywood came to town: a history of moviemaking in Utah, 1st, Layton, Utah: Gibbs Smith. ISBN 9781423605874. 
  19. Lunsford, J. Lynn. "Filming air combat is as risky as a dogfight", Pittsburgh Post-Gazette, 22 September 2006. 
  20. 20.0 20.1 Template:Cite episode
  21. Hume, 124
  22. Hume, 125
  23. 23.0 23.1 Burlingame, Jon (2012). The Music of James Bond. New York: Oxford University Press, page 156–163. ISBN 978-019-986330-3. 
  24. Fiegel, Eddi (2012). John Barry: A Sixties Theme: From James Bond to Midnight Cowboy. Faber & Faber, page 207. ISBN 978-0571299119. 
  25. Filmtrack's editorial on the Octopussy soundtrack.
  26. Moore, p. 210
  27. Octopussy. The Numbers. Nash Information Services, LLC..
  28. James Bond Movies at the Box Office. Box Office Mojo.
  29. 1984 Saturn Awards. IMDb.
  30. 1984 Golden Reel Awards. IMDb.
  31. Octopussy (1984) (de). Goldene Leinwand.
  32. Arnold, Gary. "Octopussy", 10 June 1983. 
  33. Canby, Vincent. "Film: James Bond Meets 'Octopussy'", The New York Times, 10 June 1983, p. C17. 
  34. Siskel, Gene. (10 June 1983). "Action galore saves weak 'Octopussy' script --[[ ---------------------------------- Lua module implementing the Template loop detected: Template:Webarchive template. A merger of the functionality of three templates: Template:Wayback, Template:Webcite and Template:Cite archives ]] --[[--------------------------< D E P E N D E N C I E S >------------------------------------------------------ ]] require('Module:No globals'); local getArgs = require ('Module:Arguments').getArgs; --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local categories = {}; -- category names local config = {}; -- global configuration settings local digits = {}; -- for i18n; table that translates local-wiki digits to western digits local err_warn_msgs = {}; -- error and warning messages local excepted_pages = {}; local month_num = {}; -- for i18n; table that translates local-wiki month names to western digits local prefixes = {}; -- service provider tail string prefixes local services = {}; -- archive service provider data from local s_text = {}; -- table of static text strings used to build final rendering local uncategorized_namespaces = {}; -- list of namespaces that we should not categorize local uncategorized_subpages = {}; -- list of subpages that should not be categorized --[[--------------------------< P A G E S C O P E I D E N T I F I E R S >---------------------------------- ]] local non_western_digits; -- boolean flag set true when data.digits.enable is true local this_page = mw.title.getCurrentTitle(); local track = {}; -- Associative array to hold tracking categories local ulx = {}; -- Associative array to hold template data --[[--------------------------< S U B S T I T U T E >---------------------------------------------------------- Populates numbered arguments in a message string using an argument table. ]] local function substitute (msg, args) return args and mw.message.newRawMessage (msg, args):plain() or msg; end --[[--------------------------< tableLength >----------------------- Given a 1-D table, return number of elements ]] local function tableLength(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end --[=[-------------------------< M A K E _ W I K I L I N K >---------------------------------------------------- Makes a wikilink; when both link and display text is provided, returns a wikilink in the form D; if only link is provided, returns a wikilink in the form L; if neither are provided or link is omitted, returns an empty string. ]=] local function make_wikilink (link, display, no_link) if nil == no_link then if link and ( ~= link) then if display and ( ~= display) then return table.concat ({'', display, ''}); else return table.concat ({'', link, ''}); end end return display or ; -- link not set so return the display text else -- no_link if display and ( ~= display) then -- if there is display text return display; -- return that else return link or ; -- return the target article name or empty string end end end --[[--------------------------< createTracking >----------------------- Return data in track[] ie. tracking categories ]] local function createTracking() if not excepted_pages[this_page.fullText] then -- namespace:title/fragment is allowed to be categorized (typically this module's / template's testcases page(s)) if uncategorized_namespaces[this_page.nsText] then return ; -- this page not to be categorized so return empty string end for _,v in ipairs (uncategorized_subpages) do -- cycle through page name patterns if this_page.text:match (v) then -- test page name against each pattern return ; -- this subpage type not to be categorized so return empty string end end end local out = {}; if tableLength(track) > 0 then for key, _ in pairs(track) do -- loop through table table.insert (out, make_wikilink (key)); -- and convert category names to links end end return table.concat (out); -- concat into one big string; empty string if table is empty end --[[--------------------------< inlineError >----------------------- Critical error. Render output completely in red. Add to tracking category. This function called as the last thing before abandoning this module ]] local function inlineError (msg, args) track[categories.error] = 1 return table.concat ({ 'Error in ', -- open the error message span config.tname, -- insert the local language template name ' template: ', substitute (msg, args), -- insert the formatted error message '.', -- close the span createTracking() -- add the category }) end --[[--------------------------< inlineRed >----------------------- Render a text fragment in red, such as a warning as part of the final output. Add tracking category. ]] local function inlineRed(msg, trackmsg) if trackmsg == "warning" then track[categories.warning] = 1; elseif trackmsg == "error" then track[categories.error] = 1; end return '' .. msg .. '' end --[[--------------------------< base62 >----------------------- Convert base-62 to base-10 Credit: https://de.wikipedia.org/wiki/Modul:Expr ]] local function base62( value ) local r = 1 -- default return value is input value is malformed if value:match ('%W') then -- value must only be in the set [0-9a-zA-Z] return; -- nil return when value contains extraneous characters end local n = #value -- number of characters in value local k = 1 local c r = 0 for i = n, 1, -1 do -- loop through all characters in value from ls digit to ms digit c = value:byte( i, i ) if c >= 48 and c <= 57 then -- character is digit 0-9 c = c - 48 elseif c >= 65 and c <= 90 then -- character is ascii a-z c = c - 55 else -- must be ascii A-Z c = c - 61 end r = r + c * k -- accumulate this base62 character's value k = k * 62 -- bump for next end -- for i return r end --[[--------------------------< D E C O D E _ D A T E >-------------------------------------------------------- Given a date string, return it in iso format along with an indicator of the date's format. Except that month names must be recognizable as legitimate month names with proper capitalization, and that the date string must match one of the recognized date formats, no error checking is done here; return nil else ]] local function decode_date (date_str) local patterns = { ['dmy'] = {'^(%d%d?) +([^%s%d]+) +(%d%d%d%d)$', 'd', 'm', 'y'}, -- %a does not recognize unicode combining characters used by some languages ['mdy'] = {'^([^%s%d]+) (%d%d?), +(%d%d%d%d)$', 'm', 'd', 'y'}, ['ymd'] = {'^(%d%d%d%d) +([^%s%d]+) (%d%d?)$', 'y', 'm', 'd'}, -- not mos compliant at en.wiki but may be acceptible at other wikis }; local t = {}; if non_western_digits then -- this wiki uses non-western digits? date_str = mw.ustring.gsub (date_str, '%d', digits); -- convert this wiki's non-western digits to western digits end if date_str:match ('^%d%d%d%d%-%d%d%-%d%d$') then -- already an iso format date, return western digits form return date_str, 'iso'; end for k, v in pairs (patterns) do local c1, c2, c3 = mw.ustring.match (date_str, patterns[k][1]); -- c1 .. c3 are captured but we don't know what they hold if c1 then -- set on match t = { -- translate unspecified captures to y, m, and d [patterns[k][2]] = c1, -- fill the table of captures with the captures [patterns[k][3]] = c2, -- take index names from src_pattern table and assign sequential captures [patterns[k][4]] = c3, }; if month_num[t.m] then -- when month not already a number t.m = month_num[t.m]; -- replace valid month name with a number else return nil, 'iso'; -- not a valid date form because month not valid end return mw.ustring.format ('%.4d-%.2d-%.2d', t.y, t.m, t.d), k; -- return date in iso format end end return nil, 'iso'; -- date could not be decoded; return nil and default iso date end --[[--------------------------< makeDate >----------------------- Given year, month, day numbers, (zero-padded or not) return a full date in df format where df may be one of: mdy, dmy, iso, ymd on entry, year, month, day are presumed to be correct for the date that they represent; all are required in this module, makeDate() is sometimes given an iso-format date in year: makeDate (2018-09-20, nil, nil, df) this works because table.concat() sees only one table member ]] local function makeDate (year, month, day, df) local format = { ['dmy'] = 'j F Y', ['mdy'] = 'F j, Y', ['ymd'] = 'Y F j', ['iso'] = 'Y-m-d', }; local date = table.concat ({year, month, day}, '-'); -- assemble year-initial numeric-format date (zero padding not required here) if non_western_digits then -- this wiki uses non-western digits? date = mw.ustring.gsub (date, '%d', digits); -- convert this wiki's non-western digits to western digits end return mw.getContentLanguage():formatDate (format[df], date); end --[[--------------------------< I S _ V A L I D _ D A T E >---------------------------------------------------- Returns true if date is after 31 December 1899 (why is 1900 the min year? shouldn't the internet's date-of-birth be min year?), not after today's date, and represents a valid date (29 February 2017 is not a valid date). Applies Gregorian leapyear rules. all arguments are required ]] local function is_valid_date (year, month, day) local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local month_length; local y, m, d; local today = os.date ('*t'); -- fetch a table of current date parts if not year or == year or not month or == month or not day or == day then return false; -- something missing end y = tonumber (year); m = tonumber (month); d = tonumber (day); if 1900 > y or today.year < y or 1 > m or 12 < m then -- year and month are within bounds TODO: 1900? return false; end if (2==m) then -- if February month_length = 28; -- then 28 days unless if (0==(y%4) and (0~=(y%100) or 0==(y%400))) then -- is a leap year? month_length = 29; -- if leap year then 29 days in February end else month_length=days_in_month[m]; end if 1 > d or month_length < d then -- day is within bounds return false; end -- here when date parts represent a valid date return os.time({['year']=y, ['month']=m, ['day']=d, ['hour']=0}) <= os.time(); -- date at midnight must be less than or equal to current date/time end --[[--------------------------< decodeWebciteDate >----------------------- Given a URI-path to Webcite (eg. /67xHmVFWP) return the encoded date in df format returns date string in df format - webcite date is a unix timestamp encoded as bae62 or the string 'query' ]] local function decodeWebciteDate(path, df) local dt = {}; local decode; dt = mw.text.split(path, "/") -- valid URL formats that are not base62 -- http://www.webcitation.org/query?id=1138911916587475 -- http://www.webcitation.org/query?url=http..&date=2012-06-01+21:40:03 -- http://www.webcitation.org/1138911916587475 -- http://www.webcitation.org/cache/73e53dd1f16cf8c5da298418d2a6e452870cf50e -- http://www.webcitation.org/getfile.php?fileid=1c46e791d68e89e12d0c2532cc3cf629b8bc8c8e if dt[2]:find ('query', 1, true) or dt[2]:find ('cache', 1, true) or dt[2]:find ('getfile', 1, true) or tonumber(dt[2]) then return 'query'; end decode = base62(dt[2]); -- base62 string -> exponential number if not decode then return nil; -- nil return when dt[2] contains characters not in %w end dt = os.date('*t', string.format("%d", decode):sub(1,10)) -- exponential number -> text -> first 10 characters (a unix timestamp) -> a table of date parts decode = makeDate (dt.year, dt.month, dt.day, 'iso'); -- date comparisons are all done in iso format with western digits if non_western_digits then -- this wiki uses non-western digits? decode = mw.ustring.gsub (decode, '%d', digits); -- convert this wiki's non-western digits to western digits end return decode; end --[[--------------------------< decodeWaybackDate >----------------------- Given a URI-path to Wayback (eg. /web/20160901010101/http://example.com ) or Library of Congress Web Archives (eg. /all/20160901010101/http://example.com) or UK Government Web Archive (eg. /ukgwa/20160901010101/http://example.com or /tna/20160901010101/http://example.com) return the formatted date eg. "September 1, 2016" in df format Handle non-digits in snapshot ID such as "re_" and "-" and "*" returns two values: first value is one of these: valid date string in df format - wayback date is valid (including the text string 'index' when date is '/*/') empty string - wayback date is malformed (less than 8 digits, not a valid date) nil - wayback date is '/save/' or otherwise not a number second return value is an appropriate 'message' may or may not be formatted ]] local function decodeWaybackDate(path, df) local msg, snapdate; snapdate = path:gsub ('^/web/', ):gsub ('^/all/', ):gsub ('^/ukgwa/', ):gsub ('^/tna/', ):gsub ('^/', ); -- remove leading /web/, /all/, /ukgwa/, /tna/, or / snapdate = snapdate:match ('^[^/]+'); -- get timestamp if snapdate == "*" then -- eg. /web/*/http.., etc. return 'index'; -- return indicator that this url has an index date end snapdate = snapdate:gsub ('%a%a_%d?$', ):gsub ('%-', ); -- from date, remove any trailing "re_", dashes msg = ; if snapdate:match ('%*$') then -- a trailing '*' causes calendar display at archive .org snapdate = snapdate:gsub ('%*$', ); -- remove so not part of length calc later msg = inlineRed (err_warn_msgs.ts_cal, 'warning'); -- make a message end if not tonumber(snapdate) then return nil, 'ts_nan'; -- return nil (fatal error flag) and message selector end local dlen = snapdate:len(); if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits? return , inlineRed (err_warn_msgs.ts_short, 'error'); -- return empty string and error message end local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here if not is_valid_date (year, month, day) then return , inlineRed (err_warn_msgs.ts_date, 'error'); -- return empty string and error message end snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format if 14 == dlen then return snapdate, msg; -- return date with message if any else return snapdate, msg .. inlineRed (err_warn_msgs.ts_len, 'warning'); -- return date with warning message(s) end end --[[--------------------------< decodeArchiveisDate >----------------------- Given an Archive.is "long link" URI-path (e.g. /2016.08.28-144552/http://example.com) return the date in df format (e.g. if df = dmy, return 28 August 2016) Handles "." and "-" in snapshot date, so 2016.08.28-144552 is same as 20160828144552 returns two values: first value is one of these: valid date string in df format - archive.is date is valid (including the text string 'short link' when url is the short form) empty string - wayback date is malformed (not a number, less than 8 digits, not a valid date) nil - wayback date is '/save/' second return value is an appropriate 'message' may or may not be formatted ]] local function decodeArchiveisDate(path, df) local snapdate if path:match ('^/%w+$') then -- short form url path is '/' followed by some number of base 62 digits and nothing else return "short link" -- e.g. http://archive.is/hD1qz end snapdate = mw.text.split (path, '/')[2]:gsub('[%.%-]', ); -- get snapshot date, e.g. 2016.08.28-144552; remove periods and hyphens local dlen = string.len(snapdate) if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits? return , inlineRed (err_warn_msgs.ts_short, 'error'); -- return empty string and error message end local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here if not is_valid_date (year, month, day) then return , inlineRed (err_warn_msgs.ts_date, 'error'); -- return empty string and error message end snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format if 14 == dlen then return snapdate; -- return date else return snapdate, inlineRed (err_warn_msgs.ts_len, 'warning'); -- return date with warning message end end --[[--------------------------< serviceName >----------------------- Given a domain extracted by mw.uri.new() (eg. web.archive.org) set tail string and service ID ]] local function serviceName(host, no_link) local tracking; local index; host = host:lower():gsub ('^web%.(.+)', '%1'):gsub ('^www%.(.+)', '%1'); -- lowercase, remove web. and www. subdomains if services[host] then index = host; else for k, _ in pairs (services) do if host:find ('%f[%a]'..k:gsub ('([%.%-])', '%%%1')) then index = k; break; end end end if index then local out = {}; -- empty string in [1] so that concatenated result has leading single space ulx.url1.service = services[index][4] or 'other'; tracking = services[index][5] or categories.other; -- build tail string if false == services[index][1] then -- select prefix table.insert (out, prefixes.at); elseif true == services[index][1] then table.insert (out, prefixes.atthe); else table.insert (out, services[index][1]); end table.insert (out, make_wikilink (services[index][2], services[index][3], no_link)); -- add article wikilink if services[index][6] then -- add tail postfix if it exists table.insert (out, services[index][6]); end ulx.url1.tail = table.concat (out, ' '); -- put it all together; result has leading space character else -- here when unknown archive ulx.url1.service = 'other'; tracking = categories.unknown; ulx.url1.tail = table.concat ({, prefixes.at, host, inlineRed (err_warn_msgs.unknown_url, error)}, ' '); end track[tracking] = 1 end --[[--------------------------< parseExtraArgs >----------------------- Parse numbered arguments starting at 2, such as url2..url10, date2..date10, title2..title10 For example: Template loop detected: Template:Webarchive Three url arguments not in numeric sequence (1..4..7). Function only processes arguments numbered 2 or greater (in this case 4 and 7) It creates numeric sequenced table entries like: urlx.url2.url = <argument value for url4> urlx.url3.url = <argument value for url7> Returns the number of URL arguments found numbered 2 or greater (in this case returns "2") ]] local function parseExtraArgs(args) local i, j, argurl, argurl2, argdate, argtitle j = 2 for i = 2, config.maxurls do argurl = "url" .. i if args[argurl] then argurl2 = "url" .. j ulx[argurl2] = {} ulx[argurl2]["url"] = args[argurl] argdate = "date" .. i if args[argdate] then ulx[argurl2]["date"] = args[argdate] else ulx[argurl2]["date"] = inlineRed (err_warn_msgs.date_miss, 'warning'); end argtitle = "title" .. i if args[argtitle] then ulx[argurl2]["title"] = args[argtitle] else ulx[argurl2]["title"] = nil end j = j + 1 end end if j == 2 then return 0 else return j - 2 end end --[[--------------------------< comma >----------------------- Given a date string, return "," if it's MDY ]] local function comma(date) return (date and date:match ('%a+ +%d%d?(,) +%d%d%d%d')) or ; end --[[--------------------------< createRendering >----------------------- Return a rendering of the data in ulx[][] ]] local function createRendering() local displayfield local out = {}; local index_date, msg = ulx.url1.date:match ('(index)(.*)'); -- when ulx.url1.date extract 'index' text and message text (if there is a message) ulx.url1.date = ulx.url1.date:gsub ('index.*', 'index'); -- remove message if 'none' == ulx.url1.format then -- For Template:Wayback, Template:Webcite table.insert (out, '['); -- open extlink markup table.insert (out, ulx.url1.url); -- add url if ulx.url1.title then table.insert (out, ' ') -- the required space table.insert (out, ulx.url1.title) -- the title table.insert (out, ']'); -- close extlink markup table.insert (out, ulx.url1.tail); -- tail text if ulx.url1.date then table.insert (out, ' ('); -- open date text; TODO: why the html entity? replace with regular space? table.insert (out, 'index' == ulx.url1.date and s_text.archive or s_text.archived); -- add text table.insert (out, ' '); -- insert a space table.insert (out, ulx.url1.date); -- add date table.insert (out, ')'); -- close date text end else -- no title if index_date then -- when url date is 'index' table.insert (out, table.concat ({' ', s_text.Archive_index, ']'})); -- add the index link label table.insert (out, msg or ); -- add date mismatch message when url date is /*/ and |date= has valid date else table.insert (out, table.concat ({' ', s_text.Archived, '] '})); -- add link label for url has timestamp date (will include mismatch message if there is one) end if ulx.url1.date then if 'index' ~= ulx.url1.date then table.insert (out, ulx.url1.date); -- add date when data is not 'index' end table.insert (out, comma(ulx.url1.date)); -- add ',' if date format is mdy table.insert (out, ulx.url1.tail); -- add tail text else -- no date table.insert (out, ulx.url1.tail); -- add tail text end end if 0 < ulx.url1.extraurls then -- For multiple archive URLs local tot = ulx.url1.extraurls + 1 table.insert (out, '.') -- terminate first url table.insert (out, table.concat ({' ', s_text.addlarchives, ': '})); -- add header text for i=2, tot do -- loop through the additionals local index = table.concat ({'url', i}); -- make an index displayfield = ulx[index]['title'] and 'title' or 'date'; -- choose display text table.insert (out, '['); -- open extlink markup table.insert (out, ulx[index]['url']); -- add the url table.insert (out, ' '); -- the required space table.insert (out, ulx[index][displayfield]); -- add the label table.insert (out, ']'); -- close extlink markup table.insert (out, i==tot and '.' or ', '); -- add terminator end end return table.concat (out); -- make a big string and done else -- For Template:Cite archives if 'addlarchives' == ulx.url1.format then -- Multiple archive services table.insert (out, table.concat ({s_text.addlarchives, ': '})); -- add header text else -- Multiple pages from the same archive table.insert (out, table.concat ({s_text.addlpages, ' '})); -- add header text table.insert (out, ulx.url1.date); -- add date to header text table.insert (out, ': '); -- close header text end local tot = ulx.url1.extraurls + 1; for i=1, tot do -- loop through the additionals local index = table.concat ({'url', i}); -- make an index table.insert (out, '['); -- open extlink markup table.insert (out, ulx[index]['url']); -- add url table.insert (out, ' '); -- add required space displayfield = ulx[index]['title']; if 'addlarchives' == ulx.url1.format then if not displayfield then displayfield = ulx[index]['date'] end else -- must be addlpages if not displayfield then displayfield = table.concat ({s_text.Page, ' ', i}); end end table.insert (out, displayfield); -- add title, date, page label text table.insert (out, ']'); -- close extlink markup table.insert (out, (i==tot and '.' or ', ')); -- add terminator end return table.concat (out); -- make a big string and done end end --[[--------------------------< P A R A M E T E R _ N A M E _ X L A T E >-------------------------------------- for internaltionalization, translate local-language parameter names to their English equivalents TODO: return error message if multiple aliases of the same canonical parameter name are found? returns two tables: new_args - holds canonical form parameters and their values either from translation or because the parameter was already in canonical form origin - maps canonical-form parameter names to their untranslated (local language) form for error messaging in the local language unrecognized parameters are ignored ]] local function parameter_name_xlate (args, params, enum_params) local name; -- holds modifiable name of the parameter name during evaluation local enum; -- for enumerated parameters, holds the enumerator during evaluation local found = false; -- flag used to break out of nested for loops local new_args = {}; -- a table that holds canonical and translated parameter k/v pairs local origin = {}; -- a table that maps original (local language) parameter names to their canonical name for local language error messaging local unnamed_params; -- set true when unsupported positional parameters are detected for k, v in pairs (args) do -- loop through all of the arguments in the args table name = k; -- copy of original parameter name if 'string' == type (k) then if non_western_digits then -- true when non-western digits supported at this wiki name = mw.ustring.gsub (name, '%d', digits); -- convert this wiki's non-western digits to western digits end enum = name:match ('%d+$'); -- get parameter enumerator if it exists; nil else if not enum then -- no enumerator so looking for non-enumnerated parameters -- TODO: insert shortcut here? if params[name] then name holds the canonical parameter name; no need to search further for pname, aliases in pairs (params) do -- loop through each parameter the params table for _, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table if name == alias then new_args[pname] = v; -- create a new entry in the new_args table origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name found = true; -- flag so that we can break out of these nested for loops break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair end end if found then -- true when we found an alias that matched name found = false; -- reset the flag break; -- go do next args k/v pair end end else -- enumerated parameters name = name:gsub ('%d$', '#'); -- replace enumeration digits with place holder for table search -- TODO: insert shortcut here? if num_params[name] then name holds the canonical parameter name; no need to search further for pname, aliases in pairs (enum_params) do -- loop through each parameter the num_params table for _, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table if name == alias then pname = pname:gsub ('#$', enum); -- replace the '#' place holder with the actual enumerator new_args[pname] = v; -- create a new entry in the new_args table origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name found = true; -- flag so that we can break out of these nested for loops break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair end end if found then -- true when we found an alias that matched name found = false; -- reset the flag break; -- go do next args k/v pair end end end else unnamed_params = true; -- flag for unsupported positional parameters end end -- for k, v return new_args, origin, unnamed_params; end --[[--------------------------< W E B A R C H I V E >---------------------------------------------------------- template entry point ]] local function webarchive(frame) local args = getArgs (frame); local data = mw.loadData (table.concat ({ -- make a data module name; sandbox or live 'Module:Webarchive/data', frame:getTitle():find('sandbox', 1, true) and '/sandbox' or -- this instance is ./sandbox then append /sandbox })); categories = data.categories; -- fill in the forward declarations config = data.config; if data.digits.enable then digits = data.digits; -- for i18n; table of digits in the local wiki's language non_western_digits = true; -- use_non_western_digits end err_warn_msgs = data.err_warn_msgs; excepted_pages = data.excepted_pages; month_num = data.month_num; -- for i18n; table of month names in the local wiki's language prefixes = data.prefixes; services = data.services; s_text = data.s_text; uncategorized_namespaces = data.uncategorized_namespaces; uncategorized_subpages = data.uncategorized_subpages; local origin = {}; -- holds a map of English to local language parameter names used in the current template; not currently used local unnamed_params; -- boolean set to true when template call has unnamed parameters args, origin, unnamed_params = parameter_name_xlate (args, data.params, data.enum_params); -- translate parameter names in args to English local date, format, msg, udate, uri, url; local ldf = 'iso'; -- when there is no |date= parameter, render url dates in iso format if args.url and args.url1 then -- URL argument (first) return inlineError (data.crit_err_msgs.conflicting, {origin.url, origin.url1}); end url = args.url or args.url1; if not url then return inlineError (data.crit_err_msgs.empty); end -- these iabot bugs perportedly fixed; removing these causes lua script error --[[ -- at Template:Webarchive/testcases/Production; resolve that before deleting these tests if mw.ustring.find( url, "https://web.http", 1, true ) then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred track[categories.error] = 1; return inlineError (data.crit_err_msgs.iabot1); end if url == "https://web.archive.org/http:/" then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred track[categories.error] = 1; return inlineError (data.crit_err_msgs.iabot2); end ]] if not (url:lower():find ('^http') or url:find ('^//')) then return inlineError (data.crit_err_msgs.invalid_url ); end ulx.url1 = {} ulx.url1.url = url ulx.url1.extraurls = parseExtraArgs(args) local good = false; good, uri = pcall (mw.uri.new, ulx.url1.url); -- get a table of uri parts from this url; protected mode to prevent lua error when ulx.url1.url is malformed if not good or nil == uri.host then -- abandon when ulx.url1.url is malformed return inlineError (data.crit_err_msgs.invalid_url); end serviceName(uri.host, args.nolink) if args.date and args.date1 then -- Date argument return inlineError (data.crit_err_msgs.conflicting, {origin.date, origin.date1}); end date = args.date or args.date1; date = date and date:gsub (' +', ' '); -- replace multiple spaces with a single space if date and config.verifydates then if '*' == date then date = 'index'; ldf = 'iso'; -- set to default format elseif 'mdy' == date then date = nil; -- if date extracted from URL, ldf = 'mdy'; -- then |date=mdy overrides iso elseif 'dmy' == date then date = nil; -- if date extracted from URL, ldf = 'dmy'; -- then |date=dmy overrides iso elseif 'ymd' == date then date = nil; -- if date extracted from URL, ldf = 'ymd'; -- then |date=ymd overrides iso else date, ldf = decode_date (date); -- get an iso format date from date and get date's original format end end if 'wayback' == ulx.url1.service or 'locwebarchives' == ulx.url1.service or 'ukgwa' == ulx.url1.service then if date then if config.verifydates then if ldf then udate, msg = decodeWaybackDate (uri.path); -- get the url date in iso format and format of date in |date=; 'index' when wayback url date is * if not udate then -- this is the only 'fatal' error return return inlineError (data.crit_err_msgs[msg]); end if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), -- add warning message msg, -- add message if there is one }); end end end else -- no |date= udate, msg = decodeWaybackDate (uri.path); if not udate then -- this is the only 'fatal' error return return inlineError (data.crit_err_msgs[msg]); end if == udate then date = nil; -- unset else date = udate; end end elseif 'webcite' == ulx.url1.service then if date then if config.verifydates then if ldf then udate = decodeWebciteDate (uri.path); -- get the url date in iso format if 'query' ~= udate then -- skip if query if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), }); end end end end else date = decodeWebciteDate( uri.path, "iso" ) if date == "query" then date = nil; -- unset msg = inlineRed (err_warn_msgs.date_miss, 'warning'); elseif not date then -- invalid base62 string date = inlineRed (err_warn_msgs.date1, 'error'); end end elseif 'archiveis' == ulx.url1.service then if date then if config.verifydates then if ldf then udate, msg = decodeArchiveisDate (uri.path) -- get the url date in iso format if 'short link' ~= udate then -- skip if short link if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), -- add warning message msg, -- add message if there is one }); end end end end else -- no |date= udate, msg = decodeArchiveisDate( uri.path, "iso" ) if udate == "short link" then date = nil; -- unset msg = inlineRed (err_warn_msgs.date_miss, 'warning'); elseif == udate then date = nil; -- unset else date = udate; end end else -- some other service if not date then msg = inlineRed (err_warn_msgs.date_miss, 'warning'); end end if 'index' == date then ulx.url1.date = date .. (msg or ); -- create index + message (if there is one) elseif date then ulx.url1.date = makeDate (date, nil, nil, ldf) .. (msg or ); -- create a date in the wiki's local language + message (if there is one) else ulx.url1.date = msg; end format = args.format; -- Format argument if not format then format = "none" else for k, v in pairs (data.format_vals) do -- |format= accepts two specific values loop through a table of those values local found; -- declare a nil flag for _, p in ipairs (v) do -- loop through local language variants if format == p then -- when |format= value matches format = k; -- use name from table key found = true; -- declare found so that we can break out of outer for loop break; -- break out of inner for loop end end if found then break; end end if format == "addlpages" then if not ulx.url1.date then format = "none" end elseif format == "addlarchives" then format = "addlarchives" else format = "none" end end ulx.url1.format = format if args.title and args.title1 then -- Title argument return inlineError (data.crit_err_msgs.conflicting, {origin.title, origin.title1}); end ulx.url1.title = args.title or args.title1; local rend = createRendering() if not rend then return inlineError (data.crit_err_msgs.unknown); end return rend .. ((unnamed_params and inlineRed (err_warn_msgs.unnamed_params, 'warning')) or ) .. createTracking(); end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return {webarchive = webarchive};". The Chicago Tribune. Section 3, pg. 1 – via Newspapers.com.
  35. "Film Reviews: Octopussy", Variety, 8 June 1983. 
  36. Thomas, Kevin. (10 June 1983). "'Octopussy' Fulfills The 007 Formula --[[ ---------------------------------- Lua module implementing the Template loop detected: Template:Webarchive template. A merger of the functionality of three templates: Template:Wayback, Template:Webcite and Template:Cite archives ]] --[[--------------------------< D E P E N D E N C I E S >------------------------------------------------------ ]] require('Module:No globals'); local getArgs = require ('Module:Arguments').getArgs; --[[--------------------------< F O R W A R D D E C L A R A T I O N S >-------------------------------------- ]] local categories = {}; -- category names local config = {}; -- global configuration settings local digits = {}; -- for i18n; table that translates local-wiki digits to western digits local err_warn_msgs = {}; -- error and warning messages local excepted_pages = {}; local month_num = {}; -- for i18n; table that translates local-wiki month names to western digits local prefixes = {}; -- service provider tail string prefixes local services = {}; -- archive service provider data from local s_text = {}; -- table of static text strings used to build final rendering local uncategorized_namespaces = {}; -- list of namespaces that we should not categorize local uncategorized_subpages = {}; -- list of subpages that should not be categorized --[[--------------------------< P A G E S C O P E I D E N T I F I E R S >---------------------------------- ]] local non_western_digits; -- boolean flag set true when data.digits.enable is true local this_page = mw.title.getCurrentTitle(); local track = {}; -- Associative array to hold tracking categories local ulx = {}; -- Associative array to hold template data --[[--------------------------< S U B S T I T U T E >---------------------------------------------------------- Populates numbered arguments in a message string using an argument table. ]] local function substitute (msg, args) return args and mw.message.newRawMessage (msg, args):plain() or msg; end --[[--------------------------< tableLength >----------------------- Given a 1-D table, return number of elements ]] local function tableLength(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end --[=[-------------------------< M A K E _ W I K I L I N K >---------------------------------------------------- Makes a wikilink; when both link and display text is provided, returns a wikilink in the form D; if only link is provided, returns a wikilink in the form L; if neither are provided or link is omitted, returns an empty string. ]=] local function make_wikilink (link, display, no_link) if nil == no_link then if link and ( ~= link) then if display and ( ~= display) then return table.concat ({'', display, ''}); else return table.concat ({'', link, ''}); end end return display or ; -- link not set so return the display text else -- no_link if display and ( ~= display) then -- if there is display text return display; -- return that else return link or ; -- return the target article name or empty string end end end --[[--------------------------< createTracking >----------------------- Return data in track[] ie. tracking categories ]] local function createTracking() if not excepted_pages[this_page.fullText] then -- namespace:title/fragment is allowed to be categorized (typically this module's / template's testcases page(s)) if uncategorized_namespaces[this_page.nsText] then return ; -- this page not to be categorized so return empty string end for _,v in ipairs (uncategorized_subpages) do -- cycle through page name patterns if this_page.text:match (v) then -- test page name against each pattern return ; -- this subpage type not to be categorized so return empty string end end end local out = {}; if tableLength(track) > 0 then for key, _ in pairs(track) do -- loop through table table.insert (out, make_wikilink (key)); -- and convert category names to links end end return table.concat (out); -- concat into one big string; empty string if table is empty end --[[--------------------------< inlineError >----------------------- Critical error. Render output completely in red. Add to tracking category. This function called as the last thing before abandoning this module ]] local function inlineError (msg, args) track[categories.error] = 1 return table.concat ({ 'Error in ', -- open the error message span config.tname, -- insert the local language template name ' template: ', substitute (msg, args), -- insert the formatted error message '.', -- close the span createTracking() -- add the category }) end --[[--------------------------< inlineRed >----------------------- Render a text fragment in red, such as a warning as part of the final output. Add tracking category. ]] local function inlineRed(msg, trackmsg) if trackmsg == "warning" then track[categories.warning] = 1; elseif trackmsg == "error" then track[categories.error] = 1; end return '' .. msg .. '' end --[[--------------------------< base62 >----------------------- Convert base-62 to base-10 Credit: https://de.wikipedia.org/wiki/Modul:Expr ]] local function base62( value ) local r = 1 -- default return value is input value is malformed if value:match ('%W') then -- value must only be in the set [0-9a-zA-Z] return; -- nil return when value contains extraneous characters end local n = #value -- number of characters in value local k = 1 local c r = 0 for i = n, 1, -1 do -- loop through all characters in value from ls digit to ms digit c = value:byte( i, i ) if c >= 48 and c <= 57 then -- character is digit 0-9 c = c - 48 elseif c >= 65 and c <= 90 then -- character is ascii a-z c = c - 55 else -- must be ascii A-Z c = c - 61 end r = r + c * k -- accumulate this base62 character's value k = k * 62 -- bump for next end -- for i return r end --[[--------------------------< D E C O D E _ D A T E >-------------------------------------------------------- Given a date string, return it in iso format along with an indicator of the date's format. Except that month names must be recognizable as legitimate month names with proper capitalization, and that the date string must match one of the recognized date formats, no error checking is done here; return nil else ]] local function decode_date (date_str) local patterns = { ['dmy'] = {'^(%d%d?) +([^%s%d]+) +(%d%d%d%d)$', 'd', 'm', 'y'}, -- %a does not recognize unicode combining characters used by some languages ['mdy'] = {'^([^%s%d]+) (%d%d?), +(%d%d%d%d)$', 'm', 'd', 'y'}, ['ymd'] = {'^(%d%d%d%d) +([^%s%d]+) (%d%d?)$', 'y', 'm', 'd'}, -- not mos compliant at en.wiki but may be acceptible at other wikis }; local t = {}; if non_western_digits then -- this wiki uses non-western digits? date_str = mw.ustring.gsub (date_str, '%d', digits); -- convert this wiki's non-western digits to western digits end if date_str:match ('^%d%d%d%d%-%d%d%-%d%d$') then -- already an iso format date, return western digits form return date_str, 'iso'; end for k, v in pairs (patterns) do local c1, c2, c3 = mw.ustring.match (date_str, patterns[k][1]); -- c1 .. c3 are captured but we don't know what they hold if c1 then -- set on match t = { -- translate unspecified captures to y, m, and d [patterns[k][2]] = c1, -- fill the table of captures with the captures [patterns[k][3]] = c2, -- take index names from src_pattern table and assign sequential captures [patterns[k][4]] = c3, }; if month_num[t.m] then -- when month not already a number t.m = month_num[t.m]; -- replace valid month name with a number else return nil, 'iso'; -- not a valid date form because month not valid end return mw.ustring.format ('%.4d-%.2d-%.2d', t.y, t.m, t.d), k; -- return date in iso format end end return nil, 'iso'; -- date could not be decoded; return nil and default iso date end --[[--------------------------< makeDate >----------------------- Given year, month, day numbers, (zero-padded or not) return a full date in df format where df may be one of: mdy, dmy, iso, ymd on entry, year, month, day are presumed to be correct for the date that they represent; all are required in this module, makeDate() is sometimes given an iso-format date in year: makeDate (2018-09-20, nil, nil, df) this works because table.concat() sees only one table member ]] local function makeDate (year, month, day, df) local format = { ['dmy'] = 'j F Y', ['mdy'] = 'F j, Y', ['ymd'] = 'Y F j', ['iso'] = 'Y-m-d', }; local date = table.concat ({year, month, day}, '-'); -- assemble year-initial numeric-format date (zero padding not required here) if non_western_digits then -- this wiki uses non-western digits? date = mw.ustring.gsub (date, '%d', digits); -- convert this wiki's non-western digits to western digits end return mw.getContentLanguage():formatDate (format[df], date); end --[[--------------------------< I S _ V A L I D _ D A T E >---------------------------------------------------- Returns true if date is after 31 December 1899 (why is 1900 the min year? shouldn't the internet's date-of-birth be min year?), not after today's date, and represents a valid date (29 February 2017 is not a valid date). Applies Gregorian leapyear rules. all arguments are required ]] local function is_valid_date (year, month, day) local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; local month_length; local y, m, d; local today = os.date ('*t'); -- fetch a table of current date parts if not year or == year or not month or == month or not day or == day then return false; -- something missing end y = tonumber (year); m = tonumber (month); d = tonumber (day); if 1900 > y or today.year < y or 1 > m or 12 < m then -- year and month are within bounds TODO: 1900? return false; end if (2==m) then -- if February month_length = 28; -- then 28 days unless if (0==(y%4) and (0~=(y%100) or 0==(y%400))) then -- is a leap year? month_length = 29; -- if leap year then 29 days in February end else month_length=days_in_month[m]; end if 1 > d or month_length < d then -- day is within bounds return false; end -- here when date parts represent a valid date return os.time({['year']=y, ['month']=m, ['day']=d, ['hour']=0}) <= os.time(); -- date at midnight must be less than or equal to current date/time end --[[--------------------------< decodeWebciteDate >----------------------- Given a URI-path to Webcite (eg. /67xHmVFWP) return the encoded date in df format returns date string in df format - webcite date is a unix timestamp encoded as bae62 or the string 'query' ]] local function decodeWebciteDate(path, df) local dt = {}; local decode; dt = mw.text.split(path, "/") -- valid URL formats that are not base62 -- http://www.webcitation.org/query?id=1138911916587475 -- http://www.webcitation.org/query?url=http..&date=2012-06-01+21:40:03 -- http://www.webcitation.org/1138911916587475 -- http://www.webcitation.org/cache/73e53dd1f16cf8c5da298418d2a6e452870cf50e -- http://www.webcitation.org/getfile.php?fileid=1c46e791d68e89e12d0c2532cc3cf629b8bc8c8e if dt[2]:find ('query', 1, true) or dt[2]:find ('cache', 1, true) or dt[2]:find ('getfile', 1, true) or tonumber(dt[2]) then return 'query'; end decode = base62(dt[2]); -- base62 string -> exponential number if not decode then return nil; -- nil return when dt[2] contains characters not in %w end dt = os.date('*t', string.format("%d", decode):sub(1,10)) -- exponential number -> text -> first 10 characters (a unix timestamp) -> a table of date parts decode = makeDate (dt.year, dt.month, dt.day, 'iso'); -- date comparisons are all done in iso format with western digits if non_western_digits then -- this wiki uses non-western digits? decode = mw.ustring.gsub (decode, '%d', digits); -- convert this wiki's non-western digits to western digits end return decode; end --[[--------------------------< decodeWaybackDate >----------------------- Given a URI-path to Wayback (eg. /web/20160901010101/http://example.com ) or Library of Congress Web Archives (eg. /all/20160901010101/http://example.com) or UK Government Web Archive (eg. /ukgwa/20160901010101/http://example.com or /tna/20160901010101/http://example.com) return the formatted date eg. "September 1, 2016" in df format Handle non-digits in snapshot ID such as "re_" and "-" and "*" returns two values: first value is one of these: valid date string in df format - wayback date is valid (including the text string 'index' when date is '/*/') empty string - wayback date is malformed (less than 8 digits, not a valid date) nil - wayback date is '/save/' or otherwise not a number second return value is an appropriate 'message' may or may not be formatted ]] local function decodeWaybackDate(path, df) local msg, snapdate; snapdate = path:gsub ('^/web/', ):gsub ('^/all/', ):gsub ('^/ukgwa/', ):gsub ('^/tna/', ):gsub ('^/', ); -- remove leading /web/, /all/, /ukgwa/, /tna/, or / snapdate = snapdate:match ('^[^/]+'); -- get timestamp if snapdate == "*" then -- eg. /web/*/http.., etc. return 'index'; -- return indicator that this url has an index date end snapdate = snapdate:gsub ('%a%a_%d?$', ):gsub ('%-', ); -- from date, remove any trailing "re_", dashes msg = ; if snapdate:match ('%*$') then -- a trailing '*' causes calendar display at archive .org snapdate = snapdate:gsub ('%*$', ); -- remove so not part of length calc later msg = inlineRed (err_warn_msgs.ts_cal, 'warning'); -- make a message end if not tonumber(snapdate) then return nil, 'ts_nan'; -- return nil (fatal error flag) and message selector end local dlen = snapdate:len(); if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits? return , inlineRed (err_warn_msgs.ts_short, 'error'); -- return empty string and error message end local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here if not is_valid_date (year, month, day) then return , inlineRed (err_warn_msgs.ts_date, 'error'); -- return empty string and error message end snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format if 14 == dlen then return snapdate, msg; -- return date with message if any else return snapdate, msg .. inlineRed (err_warn_msgs.ts_len, 'warning'); -- return date with warning message(s) end end --[[--------------------------< decodeArchiveisDate >----------------------- Given an Archive.is "long link" URI-path (e.g. /2016.08.28-144552/http://example.com) return the date in df format (e.g. if df = dmy, return 28 August 2016) Handles "." and "-" in snapshot date, so 2016.08.28-144552 is same as 20160828144552 returns two values: first value is one of these: valid date string in df format - archive.is date is valid (including the text string 'short link' when url is the short form) empty string - wayback date is malformed (not a number, less than 8 digits, not a valid date) nil - wayback date is '/save/' second return value is an appropriate 'message' may or may not be formatted ]] local function decodeArchiveisDate(path, df) local snapdate if path:match ('^/%w+$') then -- short form url path is '/' followed by some number of base 62 digits and nothing else return "short link" -- e.g. http://archive.is/hD1qz end snapdate = mw.text.split (path, '/')[2]:gsub('[%.%-]', ); -- get snapshot date, e.g. 2016.08.28-144552; remove periods and hyphens local dlen = string.len(snapdate) if dlen < 8 then -- we need 8 digits TODO: but shouldn't this be testing for 14 digits? return , inlineRed (err_warn_msgs.ts_short, 'error'); -- return empty string and error message end local year, month, day = snapdate:match ('(%d%d%d%d)(%d%d)(%d%d)'); -- no need for snapdatelong here if not is_valid_date (year, month, day) then return , inlineRed (err_warn_msgs.ts_date, 'error'); -- return empty string and error message end snapdate = table.concat ({year, month, day}, '-'); -- date comparisons are all done in iso format if 14 == dlen then return snapdate; -- return date else return snapdate, inlineRed (err_warn_msgs.ts_len, 'warning'); -- return date with warning message end end --[[--------------------------< serviceName >----------------------- Given a domain extracted by mw.uri.new() (eg. web.archive.org) set tail string and service ID ]] local function serviceName(host, no_link) local tracking; local index; host = host:lower():gsub ('^web%.(.+)', '%1'):gsub ('^www%.(.+)', '%1'); -- lowercase, remove web. and www. subdomains if services[host] then index = host; else for k, _ in pairs (services) do if host:find ('%f[%a]'..k:gsub ('([%.%-])', '%%%1')) then index = k; break; end end end if index then local out = {}; -- empty string in [1] so that concatenated result has leading single space ulx.url1.service = services[index][4] or 'other'; tracking = services[index][5] or categories.other; -- build tail string if false == services[index][1] then -- select prefix table.insert (out, prefixes.at); elseif true == services[index][1] then table.insert (out, prefixes.atthe); else table.insert (out, services[index][1]); end table.insert (out, make_wikilink (services[index][2], services[index][3], no_link)); -- add article wikilink if services[index][6] then -- add tail postfix if it exists table.insert (out, services[index][6]); end ulx.url1.tail = table.concat (out, ' '); -- put it all together; result has leading space character else -- here when unknown archive ulx.url1.service = 'other'; tracking = categories.unknown; ulx.url1.tail = table.concat ({, prefixes.at, host, inlineRed (err_warn_msgs.unknown_url, error)}, ' '); end track[tracking] = 1 end --[[--------------------------< parseExtraArgs >----------------------- Parse numbered arguments starting at 2, such as url2..url10, date2..date10, title2..title10 For example: Template loop detected: Template:Webarchive Three url arguments not in numeric sequence (1..4..7). Function only processes arguments numbered 2 or greater (in this case 4 and 7) It creates numeric sequenced table entries like: urlx.url2.url = <argument value for url4> urlx.url3.url = <argument value for url7> Returns the number of URL arguments found numbered 2 or greater (in this case returns "2") ]] local function parseExtraArgs(args) local i, j, argurl, argurl2, argdate, argtitle j = 2 for i = 2, config.maxurls do argurl = "url" .. i if args[argurl] then argurl2 = "url" .. j ulx[argurl2] = {} ulx[argurl2]["url"] = args[argurl] argdate = "date" .. i if args[argdate] then ulx[argurl2]["date"] = args[argdate] else ulx[argurl2]["date"] = inlineRed (err_warn_msgs.date_miss, 'warning'); end argtitle = "title" .. i if args[argtitle] then ulx[argurl2]["title"] = args[argtitle] else ulx[argurl2]["title"] = nil end j = j + 1 end end if j == 2 then return 0 else return j - 2 end end --[[--------------------------< comma >----------------------- Given a date string, return "," if it's MDY ]] local function comma(date) return (date and date:match ('%a+ +%d%d?(,) +%d%d%d%d')) or ; end --[[--------------------------< createRendering >----------------------- Return a rendering of the data in ulx[][] ]] local function createRendering() local displayfield local out = {}; local index_date, msg = ulx.url1.date:match ('(index)(.*)'); -- when ulx.url1.date extract 'index' text and message text (if there is a message) ulx.url1.date = ulx.url1.date:gsub ('index.*', 'index'); -- remove message if 'none' == ulx.url1.format then -- For Template:Wayback, Template:Webcite table.insert (out, '['); -- open extlink markup table.insert (out, ulx.url1.url); -- add url if ulx.url1.title then table.insert (out, ' ') -- the required space table.insert (out, ulx.url1.title) -- the title table.insert (out, ']'); -- close extlink markup table.insert (out, ulx.url1.tail); -- tail text if ulx.url1.date then table.insert (out, ' ('); -- open date text; TODO: why the html entity? replace with regular space? table.insert (out, 'index' == ulx.url1.date and s_text.archive or s_text.archived); -- add text table.insert (out, ' '); -- insert a space table.insert (out, ulx.url1.date); -- add date table.insert (out, ')'); -- close date text end else -- no title if index_date then -- when url date is 'index' table.insert (out, table.concat ({' ', s_text.Archive_index, ']'})); -- add the index link label table.insert (out, msg or ); -- add date mismatch message when url date is /*/ and |date= has valid date else table.insert (out, table.concat ({' ', s_text.Archived, '] '})); -- add link label for url has timestamp date (will include mismatch message if there is one) end if ulx.url1.date then if 'index' ~= ulx.url1.date then table.insert (out, ulx.url1.date); -- add date when data is not 'index' end table.insert (out, comma(ulx.url1.date)); -- add ',' if date format is mdy table.insert (out, ulx.url1.tail); -- add tail text else -- no date table.insert (out, ulx.url1.tail); -- add tail text end end if 0 < ulx.url1.extraurls then -- For multiple archive URLs local tot = ulx.url1.extraurls + 1 table.insert (out, '.') -- terminate first url table.insert (out, table.concat ({' ', s_text.addlarchives, ': '})); -- add header text for i=2, tot do -- loop through the additionals local index = table.concat ({'url', i}); -- make an index displayfield = ulx[index]['title'] and 'title' or 'date'; -- choose display text table.insert (out, '['); -- open extlink markup table.insert (out, ulx[index]['url']); -- add the url table.insert (out, ' '); -- the required space table.insert (out, ulx[index][displayfield]); -- add the label table.insert (out, ']'); -- close extlink markup table.insert (out, i==tot and '.' or ', '); -- add terminator end end return table.concat (out); -- make a big string and done else -- For Template:Cite archives if 'addlarchives' == ulx.url1.format then -- Multiple archive services table.insert (out, table.concat ({s_text.addlarchives, ': '})); -- add header text else -- Multiple pages from the same archive table.insert (out, table.concat ({s_text.addlpages, ' '})); -- add header text table.insert (out, ulx.url1.date); -- add date to header text table.insert (out, ': '); -- close header text end local tot = ulx.url1.extraurls + 1; for i=1, tot do -- loop through the additionals local index = table.concat ({'url', i}); -- make an index table.insert (out, '['); -- open extlink markup table.insert (out, ulx[index]['url']); -- add url table.insert (out, ' '); -- add required space displayfield = ulx[index]['title']; if 'addlarchives' == ulx.url1.format then if not displayfield then displayfield = ulx[index]['date'] end else -- must be addlpages if not displayfield then displayfield = table.concat ({s_text.Page, ' ', i}); end end table.insert (out, displayfield); -- add title, date, page label text table.insert (out, ']'); -- close extlink markup table.insert (out, (i==tot and '.' or ', ')); -- add terminator end return table.concat (out); -- make a big string and done end end --[[--------------------------< P A R A M E T E R _ N A M E _ X L A T E >-------------------------------------- for internaltionalization, translate local-language parameter names to their English equivalents TODO: return error message if multiple aliases of the same canonical parameter name are found? returns two tables: new_args - holds canonical form parameters and their values either from translation or because the parameter was already in canonical form origin - maps canonical-form parameter names to their untranslated (local language) form for error messaging in the local language unrecognized parameters are ignored ]] local function parameter_name_xlate (args, params, enum_params) local name; -- holds modifiable name of the parameter name during evaluation local enum; -- for enumerated parameters, holds the enumerator during evaluation local found = false; -- flag used to break out of nested for loops local new_args = {}; -- a table that holds canonical and translated parameter k/v pairs local origin = {}; -- a table that maps original (local language) parameter names to their canonical name for local language error messaging local unnamed_params; -- set true when unsupported positional parameters are detected for k, v in pairs (args) do -- loop through all of the arguments in the args table name = k; -- copy of original parameter name if 'string' == type (k) then if non_western_digits then -- true when non-western digits supported at this wiki name = mw.ustring.gsub (name, '%d', digits); -- convert this wiki's non-western digits to western digits end enum = name:match ('%d+$'); -- get parameter enumerator if it exists; nil else if not enum then -- no enumerator so looking for non-enumnerated parameters -- TODO: insert shortcut here? if params[name] then name holds the canonical parameter name; no need to search further for pname, aliases in pairs (params) do -- loop through each parameter the params table for _, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table if name == alias then new_args[pname] = v; -- create a new entry in the new_args table origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name found = true; -- flag so that we can break out of these nested for loops break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair end end if found then -- true when we found an alias that matched name found = false; -- reset the flag break; -- go do next args k/v pair end end else -- enumerated parameters name = name:gsub ('%d$', '#'); -- replace enumeration digits with place holder for table search -- TODO: insert shortcut here? if num_params[name] then name holds the canonical parameter name; no need to search further for pname, aliases in pairs (enum_params) do -- loop through each parameter the num_params table for _, alias in ipairs (aliases) do -- loop through each alias in the parameter's aliases table if name == alias then pname = pname:gsub ('#$', enum); -- replace the '#' place holder with the actual enumerator new_args[pname] = v; -- create a new entry in the new_args table origin [pname] = k; -- create an entry to make canonical parameter name to original local language parameter name found = true; -- flag so that we can break out of these nested for loops break; -- no need to search the rest of the aliases table for name so go on to the next k, v pair end end if found then -- true when we found an alias that matched name found = false; -- reset the flag break; -- go do next args k/v pair end end end else unnamed_params = true; -- flag for unsupported positional parameters end end -- for k, v return new_args, origin, unnamed_params; end --[[--------------------------< W E B A R C H I V E >---------------------------------------------------------- template entry point ]] local function webarchive(frame) local args = getArgs (frame); local data = mw.loadData (table.concat ({ -- make a data module name; sandbox or live 'Module:Webarchive/data', frame:getTitle():find('sandbox', 1, true) and '/sandbox' or -- this instance is ./sandbox then append /sandbox })); categories = data.categories; -- fill in the forward declarations config = data.config; if data.digits.enable then digits = data.digits; -- for i18n; table of digits in the local wiki's language non_western_digits = true; -- use_non_western_digits end err_warn_msgs = data.err_warn_msgs; excepted_pages = data.excepted_pages; month_num = data.month_num; -- for i18n; table of month names in the local wiki's language prefixes = data.prefixes; services = data.services; s_text = data.s_text; uncategorized_namespaces = data.uncategorized_namespaces; uncategorized_subpages = data.uncategorized_subpages; local origin = {}; -- holds a map of English to local language parameter names used in the current template; not currently used local unnamed_params; -- boolean set to true when template call has unnamed parameters args, origin, unnamed_params = parameter_name_xlate (args, data.params, data.enum_params); -- translate parameter names in args to English local date, format, msg, udate, uri, url; local ldf = 'iso'; -- when there is no |date= parameter, render url dates in iso format if args.url and args.url1 then -- URL argument (first) return inlineError (data.crit_err_msgs.conflicting, {origin.url, origin.url1}); end url = args.url or args.url1; if not url then return inlineError (data.crit_err_msgs.empty); end -- these iabot bugs perportedly fixed; removing these causes lua script error --[[ -- at Template:Webarchive/testcases/Production; resolve that before deleting these tests if mw.ustring.find( url, "https://web.http", 1, true ) then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred track[categories.error] = 1; return inlineError (data.crit_err_msgs.iabot1); end if url == "https://web.archive.org/http:/" then -- track bug - TODO: IAbot bug; not known if the bug has been fixed; deferred track[categories.error] = 1; return inlineError (data.crit_err_msgs.iabot2); end ]] if not (url:lower():find ('^http') or url:find ('^//')) then return inlineError (data.crit_err_msgs.invalid_url ); end ulx.url1 = {} ulx.url1.url = url ulx.url1.extraurls = parseExtraArgs(args) local good = false; good, uri = pcall (mw.uri.new, ulx.url1.url); -- get a table of uri parts from this url; protected mode to prevent lua error when ulx.url1.url is malformed if not good or nil == uri.host then -- abandon when ulx.url1.url is malformed return inlineError (data.crit_err_msgs.invalid_url); end serviceName(uri.host, args.nolink) if args.date and args.date1 then -- Date argument return inlineError (data.crit_err_msgs.conflicting, {origin.date, origin.date1}); end date = args.date or args.date1; date = date and date:gsub (' +', ' '); -- replace multiple spaces with a single space if date and config.verifydates then if '*' == date then date = 'index'; ldf = 'iso'; -- set to default format elseif 'mdy' == date then date = nil; -- if date extracted from URL, ldf = 'mdy'; -- then |date=mdy overrides iso elseif 'dmy' == date then date = nil; -- if date extracted from URL, ldf = 'dmy'; -- then |date=dmy overrides iso elseif 'ymd' == date then date = nil; -- if date extracted from URL, ldf = 'ymd'; -- then |date=ymd overrides iso else date, ldf = decode_date (date); -- get an iso format date from date and get date's original format end end if 'wayback' == ulx.url1.service or 'locwebarchives' == ulx.url1.service or 'ukgwa' == ulx.url1.service then if date then if config.verifydates then if ldf then udate, msg = decodeWaybackDate (uri.path); -- get the url date in iso format and format of date in |date=; 'index' when wayback url date is * if not udate then -- this is the only 'fatal' error return return inlineError (data.crit_err_msgs[msg]); end if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), -- add warning message msg, -- add message if there is one }); end end end else -- no |date= udate, msg = decodeWaybackDate (uri.path); if not udate then -- this is the only 'fatal' error return return inlineError (data.crit_err_msgs[msg]); end if == udate then date = nil; -- unset else date = udate; end end elseif 'webcite' == ulx.url1.service then if date then if config.verifydates then if ldf then udate = decodeWebciteDate (uri.path); -- get the url date in iso format if 'query' ~= udate then -- skip if query if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), }); end end end end else date = decodeWebciteDate( uri.path, "iso" ) if date == "query" then date = nil; -- unset msg = inlineRed (err_warn_msgs.date_miss, 'warning'); elseif not date then -- invalid base62 string date = inlineRed (err_warn_msgs.date1, 'error'); end end elseif 'archiveis' == ulx.url1.service then if date then if config.verifydates then if ldf then udate, msg = decodeArchiveisDate (uri.path) -- get the url date in iso format if 'short link' ~= udate then -- skip if short link if udate ~= date then -- date comparison using iso format dates date = udate; msg = table.concat ({ inlineRed (err_warn_msgs.mismatch, 'warning'), -- add warning message msg, -- add message if there is one }); end end end end else -- no |date= udate, msg = decodeArchiveisDate( uri.path, "iso" ) if udate == "short link" then date = nil; -- unset msg = inlineRed (err_warn_msgs.date_miss, 'warning'); elseif == udate then date = nil; -- unset else date = udate; end end else -- some other service if not date then msg = inlineRed (err_warn_msgs.date_miss, 'warning'); end end if 'index' == date then ulx.url1.date = date .. (msg or ); -- create index + message (if there is one) elseif date then ulx.url1.date = makeDate (date, nil, nil, ldf) .. (msg or ); -- create a date in the wiki's local language + message (if there is one) else ulx.url1.date = msg; end format = args.format; -- Format argument if not format then format = "none" else for k, v in pairs (data.format_vals) do -- |format= accepts two specific values loop through a table of those values local found; -- declare a nil flag for _, p in ipairs (v) do -- loop through local language variants if format == p then -- when |format= value matches format = k; -- use name from table key found = true; -- declare found so that we can break out of outer for loop break; -- break out of inner for loop end end if found then break; end end if format == "addlpages" then if not ulx.url1.date then format = "none" end elseif format == "addlarchives" then format = "addlarchives" else format = "none" end end ulx.url1.format = format if args.title and args.title1 then -- Title argument return inlineError (data.crit_err_msgs.conflicting, {origin.title, origin.title1}); end ulx.url1.title = args.title or args.title1; local rend = createRendering() if not rend then return inlineError (data.crit_err_msgs.unknown); end return rend .. ((unnamed_params and inlineRed (err_warn_msgs.unnamed_params, 'warning')) or ) .. createTracking(); end --[[--------------------------< E X P O R T E D F U N C T I O N S >------------------------------------------ ]] return {webarchive = webarchive};". Los Angeles Times. Part IV, pp. 9, 15 – via Newspapers.com.
  37. Octopussy: Review on Reelviews. James Berardinelli.
  38. 23 BEST (AND WORST) JAMES BOND MOVIES. E!.
  39. Svetkey, Benjamin. Countdown: Ranking the Bond Films. Entertainment Weekly.
  40. Wilner, Norman. Rating the Spy Game. MSN.
  41. James Bond's Top 20. IGN Entertainment.
  42. 42.0 42.1 Henderson, C.J. (September–October 1983). "Capsule Reviews". The Space Gamer (Steve Jackson Games) (65): 38–39. 
  43. Octopussy. Rotten Tomatoes.
  44. Berham, Debbie (30 August 2001). Octopussy: Review. BBC.
  45. David Williams (16 February 2015). Why Octopussy is the best (and possibly worst) James Bond film. GQ.
  46. Peary, Danny (1986). Guide for the Film Fanatic. Simon & Schuster, page 306–7. 
  47. Morgan, Kim (12 November 2006). The Top 10 Bond Girls. Fandango.
  48. Rich, Joshua (13 November 2006). Countdown: The 10 Worst Bond Girls. Entertainment Weekly.
  49. Nashawaty, Chris (12 December 2008). "Moore ... and Sometimes Less". Entertainment Weekly (1025). http://www.ew.com/ew/article/0,,20244432,00.html. Retrieved 9 August 2011. 
  50. Denise Richards Voted Worst Bond Girl Ever. Zap2it.
  51. Parfitt, Orlando (24 September 2012). James Bond at 50: the best Bond Girl names.

External links[]


v - e - d
Media
Films: Dr. NoFrom Russia with LoveGoldfingerThunderballYou Only Live TwiceOn Her Majesty's Secret ServiceDiamonds Are ForeverLive and Let DieThe Man with the Golden GunThe Spy Who Loved MeMoonrakerFor Your Eyes OnlyOctopussyNever Say Never AgainA View to a KillThe Living DaylightsLicence to KillGoldenEyeTomorrow Never DiesThe World Is Not EnoughDie Another DayCasino RoyaleQuantum of SolaceSkyfallSpectreNo Time to Die

Soundtracks: Moonraker

Actors
Sean ConneryGeorge LazenbyRoger MooreTimothy DaltonPierce BrosnanDaniel CraigJohn CleeseRalph Fiennes
Songs
From Russia with LoveGoldfingerThunderballYou Only Live TwiceWe Have All the Time in the WorldDiamonds Are ForeverLive and Let DieThe Man with the Golden GunNobody Does It BetterMoonraker (original)Moonraker (disco)For Your Eyes OnlyAll Time HighNever Say Never AgainA View to a KillThe Living DaylightsIf There Was a ManLicence to KillIf You Asked Me ToGoldeneyeThe Experience of LoveTomorrow Never DiesThe World Is Not EnoughNo Time to Die
See also
Metro-Goldwyn-MayerUnited Artists